From aa7aa22850bdd8123441f877812f73502c6ead16 Mon Sep 17 00:00:00 2001 From: sagudev <16504129+sagudev@users.noreply.github.com> Date: Sun, 15 May 2022 10:53:34 +0200 Subject: [PATCH 01/75] Tests: Add AIFF tests from TagLib --- tests/taglib/data/alaw.aifc | Bin 0 -> 1890 bytes tests/taglib/data/empty.aiff | Bin 0 -> 5936 bytes tests/taglib/data/excessive_alloc.aif | Bin 0 -> 2170 bytes tests/taglib/data/segfault.aif | Bin 0 -> 31 bytes tests/taglib/main.rs | 3 + tests/taglib/test_aiff.rs | 117 ++++++++++++++++++++++++++ tests/taglib/util/mod.rs | 114 +++++++++++++++++++++++++ 7 files changed, 234 insertions(+) create mode 100644 tests/taglib/data/alaw.aifc create mode 100644 tests/taglib/data/empty.aiff create mode 100644 tests/taglib/data/excessive_alloc.aif create mode 100644 tests/taglib/data/segfault.aif create mode 100644 tests/taglib/main.rs create mode 100644 tests/taglib/test_aiff.rs create mode 100644 tests/taglib/util/mod.rs diff --git a/tests/taglib/data/alaw.aifc b/tests/taglib/data/alaw.aifc new file mode 100644 index 0000000000000000000000000000000000000000..33b4ea2a5734c10af92aa556de6c50aa102efac6 GIT binary patch literal 1890 zcmX|B2~d+~8vb*iBm@uwf(e)eaX^Vl{)7;~EUlJSP=Q*Zvn~oOpa_aq?d(j=eIJC| zaD+e(?1G$0AV6@{+3u+9+6ubuw2Kr$%28cfS9i3#`_plKXTF*5d*0`J-s_uh-@)8$ z0KgQAv-V}~JMwNW0077?$>F`32eTm@NCaR2fIb2Q?a zKcBvT7oW|hi<2skU(6QgegF~P2aub$004iYw2Xd^URhmz1_0o{BBfO&ZxO^>PR$z* z@-62Ll9iK}4`E><0Jsdhyy=CMH5b`&Sjp))nJ`pl;_ z6=mh;>Fksgb|U@%{v2`kyO1AuvAVkCL}hjHDIhN|2dW8u3_^dk**7*b^Kd3_U)FCI zlq!laDQPJ+ISExx0J&9OfuK9vuW`Fj?E_q7&M|5v|8N zJnHJ}bE=3Lse+U=He0}ArH6X_F1vetK;xV8PpAO_9m*w{NCx+S(ZuB%tOC#+?mkw^qLB{nfJR)qOMEjMZg$Gflf^f^6$hJ&1B zPDvZ_1hJB`bRC?0RoxA8l4!k_y;&JwV{y7vvcaO*gMe&I{5)xS=Oq1R_ zG-~hfx!ULUm4PWjA(IuCm_$z{{NnSwT%BG0*ZMmr8o=1Z-El0YkjWHsqD<~hy4^Y9 zH8n?Z81%R}db}`=Po&fhxcYl~dR>#ohTKqWI(>J-t~e&|c)iLnGUe{NcCE{)seu}l zA&iIautaE!XDBf4bh|n`9m7|m*eOf~BR)Q!kxqK$3AkM@$9Q03K+}K%1@W;gW{N18 zhrDhaa@srXj=+FUeFMeKV8qg6nIb+=S+ALNLSuD}jVKFHygeyQ7Cn}cMmpK98xHs# z(4fY23rN050M*A}q;i0l+DWIw;SWqr`XoOi`58i{P>`OH1_l*dy`yfs9SS~Vc!37f z8LU_qL%_iWHyQ@V98glDLmtIl8W|J`p!l$m7(9qbCL)kw&pM=1wW8J5-rjDRKO3VI&>_K`WYjw7k65Xj93h*4p(O7fUan zF9}8yNko8JIlZvUGHHS2tvZEbm779NQvbCdUQ_;AY2<_@LG zsNeLM>o3#LP`BA>86ptH|QPF7>sgN`^HH$ zmb;1bNSLYxg~mHD;Psegs+QG*F+?y;#Np#$FL7(Qqu6izmaV z71vkmuc)*>?}%2?v3NWt3JH+0SR6DyYRQ^art^7ydYPj3<@u@*43q&I-Ld-M6cr%hxjX_1i2jX5W!7p*(Nfo;JzG+I{15Ma_0Ly}|GD$s%vaZ+zxes~7y0{h z4v60oAIv(ixA4&1%^Tm`fAHmZU*Ebpee3?=V;_HfwBT4l!S8ZEdU)^Nmk(!VXa5J= CPx9XY literal 0 HcmV?d00001 diff --git a/tests/taglib/data/empty.aiff b/tests/taglib/data/empty.aiff new file mode 100644 index 0000000000000000000000000000000000000000..849b762da5a8d2b1a366ea8f9d3daa87be7a194c GIT binary patch literal 5936 zcmZvgVQwTv423(HNysh|Li~U$a10O-i66^KWUs@GI3K&&Bs;v%pWW3FjFg$~>ay$k z`Po&S`}cpo+qNHm{Q39$`(NI_%k}o%cC~F^|FwPl(>H(rn)`en9{$MH_mAGy_x0sz zyScn<*V}fvT)yP`^Y+!{al5VYr|q1vZ98T3_VOH$*UY_K+QU_zo-%&QFHdgD@;TnF za>wXZ=GI^QGY3oV^T~Ja>pSx@`n27}{wBYtdWsM3z;u&U&i#iM{krI_O z-E&1cShdOuJrgt&b*~+-=is^xzH>-?$rV|9X-x3RO0RK(Zsl)(nwh7AuZ3<WvJdIXi!x_L_8YF1(~TQkBaalwL`7+%Jk_{SIQmHOgoqJ+?7(IA7am# zGe2!_E+3<6_1kJ~JlH&y`jd~cSotZ&Gf#~=&J|j?2hsTmF|v}L`_)L%&l%w zF+R5fTW6)YsURwMDoLeU&BO{u6*_7&IuWXy&V0=y#$&Zrjw{P!0h3seAFj;9q3Ysi z#oYR+EMZ|bE905fRZ=WW-j_$cPlifo<%bzWSk+pqapfK8d0s1U(;ro0_Qic<9qlZJ zu`h2GcR6SlSH#vGn5_%f^cueXtlr?f;OcsqS{$xHj8ARlTJNo+sxeR%(;};N=qRPW z!ZKMI1mMscD^AB%SEDudgHQCd_@md!ZS#{cVypUL)xO8WOKSaNY93oLs14XdhvP}> z@lx^Nsd2d*KE@YXTeen#ow~Yt=67ClMYgPu)tAhmTuj_mv1O?uuq@TlZ$X~h+v8FA0_OjEip56-DN zuD+z!=Co73%bPpwbFbHHR;GgJu%7rB0p;#JYlWM9FD=8#WA9F=3`=Hk-gvQ`K8MDqx2SL&(V{=p%9`L8`@~HIUm^C1KWce^NcdL%@ zY_?q$%*<6yM&oYsln6P$z)^EmtW`~vPphJ;eCKm|py)s~Cp;sT!@*glA7Q*6AlY!iay zPHdXtmMw=DSTP!p)%LVMK$`>)AUR+4Ytl^@x-ps zn!(SWMeJ9sM|S00O^h1AHCeN!CxYUSfHMQ1dtJ2_N2OEk6W1tC#%i7T@V#v^Ej#S9 zo}j2w?fFigVsTXJsA=U?wfYV`HI8lVeeKN*$dV|HR$F#6xfrEeHxuWqZ~Rmq=Mj}U zc&0M-i^bl1JGVT^UvX&lR+Tbpq-~)k^SDa<6^qp~$`dEi`p&Kb$k*`b)v7^tVI&}t zT@{wQMukp2HPP$ScQUU~{3i!xS-BZS5Zy4fuHqmc?^W`u1}w$wsSJhMBZ#8oZ>)Rd z?Q6VD#O}=gpT=ra3kwkzoOIL4@VOCim9PHz0wQ8U>$X#OC5t;kZmb-lE zC^MMlA6Ca6HRsRPN6#C*$TwAM9g|<)WbWdF5$d0q#8JOlM+|#cZd;hguaS-BMg#fX z^T+&Owp8hjgvtyGJ z;)OYww1SCDotP2)$T>jguA^lD-ij;sWMbtljgwqwxoT5JLHWAtp^0^l13eM?*0IhI zSj)ILr^17#5KTRms)(sa&v{RM6aSufJP{CUD<`uGQ86^i@Z7^{^ay#CbFK6~im4vc z46CCWX~y>);Hj8CvOM~kb=(oxyTn9=4O&rkR2wW)ZCz1$26I$bRDxAv?6 literal 0 HcmV?d00001 diff --git a/tests/taglib/data/excessive_alloc.aif b/tests/taglib/data/excessive_alloc.aif new file mode 100644 index 0000000000000000000000000000000000000000..9cb3a6e10defc7bff06bfa670aae859f687b0193 GIT binary patch literal 2170 zcmcIlT}V@57=EV``LmcKNCe}f=nrExr9=>Moi$x`oX!*NCMb8-oS8>9BPwfyBB8tP zg!C@D>ME$a0;3BtyeR4-qPmIjCdxq#wfuU&?|eIdZeYw7DPb#A}pNZ&U0Ggi=wtF;1DYC2pL>B_VWYar5*4`(#~N z_<=>ZpaSz%LRFxz?*#s;%ZzLB?zWc00MlJ(PbxiV+uWDdGTP|H%-;Aug8v(&+7#`mCyzlL8=zw%sby6T?4)GR&fpIJHN>kPkJSXd++h_!Rj(%2wt8LRHnt1cf4dlqg|ZT(92Nui%@`jT1_y7 zM3`MklA&1^MobLD01Lw!GQ$*GG-@TnVlKyr>l<1Q>z)=@v!ldn+RMb%6x{;fK%-lG znYVc1Z!J5#u1x0+w-T|80KQpMS^#qWXC#Fmo#;ClWUk%d=_*o6Fzle%MC;HDh5_8Q zpv~HFqRkph+S^D=I{qcl&L9=H*{Nubz}Z_#sSql%%2kB(glU@rg2_moC)*h#D-53K z`^VD&FTXx(+ECf8$aX3eKfO-8e>YfO<7I2&qb=N5iA9UNODSZhe;^ZuE| z%5suPsa-N1QIJ{|VvgPJ?yZNlZ3Ti83xrcEv= cyi`A-?dL@P#oEN=*U17yN%yzO_lEu9HE`C_@9WC|0>Qz4EsampleFrames()); + // CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isAiffC()); +} + +#[test] +#[ignore] +fn test_aifc_properties() { + let file = lofty::read_from_path("tests/taglib/data/alaw.aifc").unwrap(); + + assert_eq!(file.file_type(), FileType::AIFF); + + let properties = file.properties(); + assert_eq!(properties.duration().as_secs(), 0); + assert_delta!(properties.duration().as_millis(), 37, 1); + assert_eq!(properties.audio_bitrate(), Some(355)); + assert_eq!(properties.sample_rate(), Some(44100)); + assert_eq!(properties.channels(), Some(1)); + assert_eq!(properties.bit_depth(), Some(16)); + // TODO: get those options in lofty + // CPPUNIT_ASSERT_EQUAL(1622U, f.audioProperties()->sampleFrames()); + // CPPUNIT_ASSERT_EQUAL(true, f.audioProperties()->isAiffC()); + // CPPUNIT_ASSERT_EQUAL(ByteVector("ALAW"), f.audioProperties()->compressionType()); + // CPPUNIT_ASSERT_EQUAL(String("SGI CCITT G.711 A-law"), f.audioProperties()->compressionName()); +} + +#[test] +#[ignore] +fn test_save_id3v2() { + let mut file = temp_file!("tests/taglib/data/empty.aiff"); + + { + let mut tfile = lofty::read_from(&mut file).unwrap(); + + assert_eq!(tfile.file_type(), FileType::AIFF); + + assert!(tfile.tag(lofty::TagType::ID3v2).is_none()); + + let mut tag = lofty::Tag::new(lofty::TagType::ID3v2); + tag.set_title("TitleXXX".to_string()); + tfile.insert_tag(tag); + file.rewind().unwrap(); + tfile.save_to(&mut file).unwrap(); + assert!(tfile.contains_tag_type(lofty::TagType::ID3v2)); + } + + file.rewind().unwrap(); + + { + let mut tfile = lofty::read_from(&mut file).unwrap(); + + assert_eq!(tfile.file_type(), FileType::AIFF); + + let mut tag = tfile.tag(lofty::TagType::ID3v2).unwrap().to_owned(); + assert_eq!(tag.title().as_deref(), Some("TitleXXX")); + tag.set_title("".to_string()); + tfile.insert_tag(tag); + file.rewind().unwrap(); + tfile.save_to(&mut file).unwrap(); + assert!(!tfile.contains_tag_type(lofty::TagType::ID3v2)); + } + + file.rewind().unwrap(); + + { + let tfile = lofty::read_from(&mut file).unwrap(); + + assert_eq!(tfile.file_type(), FileType::AIFF); + + assert!(!tfile.contains_tag_type(lofty::TagType::ID3v2)); + } +} + +// TODO: testSaveID3v23 +// TODO: testDuplicateID3v2 + +#[test] +#[ignore] +fn test_fuzzed_file1() { + assert_eq!( + get_filetype("tests/taglib/data/segfault.aif"), + FileType::AIFF + ); +} + +// the file doesn't even have a valid signature +// #[test] +// #[ignore] +// fn test_fuzzed_file2() { +// let mut file = File::open("tests/taglib/data/excessive_alloc.aif").unwrap(); +// +// let mut buf = [0; 12]; +// file.read_exact(&mut buf).unwrap(); +// +// assert_eq!(FileType::from_buffer(&buf).unwrap(), FileType::AIFF); +// } diff --git a/tests/taglib/util/mod.rs b/tests/taglib/util/mod.rs new file mode 100644 index 00000000..f81c89ca --- /dev/null +++ b/tests/taglib/util/mod.rs @@ -0,0 +1,114 @@ +/// This function tries to simulate TagLibs isValid function +// https://github.com/Serial-ATA/lofty-rs/pull/51#discussion_r873171570 +pub fn get_filetype>(path: P) -> lofty::FileType { + let mut file = std::fs::File::open(path).unwrap(); + let mut buf = [0; 12]; + std::io::Read::read_exact(&mut file, &mut buf).unwrap(); + lofty::FileType::from_buffer(&buf).unwrap() +} + +#[macro_export] +macro_rules! assert_delta { + ($x:expr, $y:expr, $d:expr) => { + if $x > $y { + assert!($x - $y <= $d) + } else if $y > $x { + assert!($y - $x <= $d) + } + }; +} + +#[macro_export] +macro_rules! temp_file { + ($path:tt) => {{ + use std::io::{Seek, Write}; + let mut file = tempfile::tempfile().unwrap(); + file.write_all(&std::fs::read($path).unwrap()).unwrap(); + + file.seek(std::io::SeekFrom::Start(0)).unwrap(); + + file + }}; +} + +#[macro_export] +macro_rules! verify_artist { + ($file:ident, $method:ident, $expected_value:literal, $item_count:expr) => {{ + println!("VERIFY: Expecting `{}` to have {} items, with an artist of \"{}\"", stringify!($method), $item_count, $expected_value); + + verify_artist!($file, $method(), $expected_value, $item_count) + }}; + ($file:ident, $method:ident, $arg:path, $expected_value:literal, $item_count:expr) => {{ + println!("VERIFY: Expecting `{}` to have {} items, with an artist of \"{}\"", stringify!($arg), $item_count, $expected_value); + + verify_artist!($file, $method($arg), $expected_value, $item_count) + }}; + ($file:ident, $method:ident($($arg:path)?), $expected_value:literal, $item_count:expr) => {{ + assert!($file.$method($(&$arg)?).is_some()); + + let tag = $file.$method($(&$arg)?).unwrap(); + + assert_eq!(tag.item_count(), $item_count); + + assert_eq!( + tag.get_item_ref(&ItemKey::TrackArtist), + Some(&TagItem::new( + ItemKey::TrackArtist, + ItemValue::Text(String::from($expected_value)) + )) + ); + + tag + }}; +} + +#[macro_export] +macro_rules! set_artist { + ($tagged_file:ident, $method:ident, $expected_value:literal, $item_count:expr => $file_write:ident, $new_value:literal) => { + let tag = verify_artist!($tagged_file, $method, $expected_value, $item_count); + println!( + "WRITE: Writing artist \"{}\" to {}\n", + $new_value, + stringify!($method) + ); + set_artist!($file_write, $new_value, tag) + }; + ($tagged_file:ident, $method:ident, $arg:path, $expected_value:literal, $item_count:expr => $file_write:ident, $new_value:literal) => { + let tag = verify_artist!($tagged_file, $method, $arg, $expected_value, $item_count); + println!( + "WRITE: Writing artist \"{}\" to {}\n", + $new_value, + stringify!($arg) + ); + set_artist!($file_write, $new_value, tag) + }; + ($file_write:ident, $new_value:literal, $tag:ident) => { + $tag.insert_item_unchecked(TagItem::new( + ItemKey::TrackArtist, + ItemValue::Text(String::from($new_value)), + )); + + $file_write.seek(std::io::SeekFrom::Start(0)).unwrap(); + + $tag.save_to(&mut $file_write).unwrap(); + }; +} + +#[macro_export] +macro_rules! remove_tag { + ($path:tt, $tag_type:path) => { + let mut file = temp_file!($path); + + let tagged_file = lofty::read_from(&mut file, false).unwrap(); + assert!(tagged_file.tag(&$tag_type).is_some()); + + file.seek(std::io::SeekFrom::Start(0)).unwrap(); + + $tag_type.remove_from(&mut file).unwrap(); + + file.seek(std::io::SeekFrom::Start(0)).unwrap(); + + let tagged_file = lofty::read_from(&mut file, false).unwrap(); + assert!(tagged_file.tag(&$tag_type).is_none()); + }; +} From 8b9a23767aa2e1f9b67701a42196db0c0d510d31 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Fri, 6 Jan 2023 23:06:50 -0500 Subject: [PATCH 02/75] Tests: Add TagLib APE tests --- tests/taglib/data/longloop.ape | Bin 0 -> 184 bytes tests/taglib/data/mac-390-hdr.ape | Bin 0 -> 128 bytes tests/taglib/data/mac-396.ape | Bin 0 -> 104 bytes tests/taglib/data/mac-399-id3v2.ape | Bin 0 -> 89155 bytes tests/taglib/data/mac-399-tagged.ape | Bin 0 -> 91591 bytes tests/taglib/data/mac-399.ape | Bin 0 -> 85212 bytes tests/taglib/data/zerodiv.ape | Bin 0 -> 946 bytes tests/taglib/main.rs | 1 + tests/taglib/test_ape.rs | 381 +++++++++++++++++++++++++++ 9 files changed, 382 insertions(+) create mode 100644 tests/taglib/data/longloop.ape create mode 100644 tests/taglib/data/mac-390-hdr.ape create mode 100644 tests/taglib/data/mac-396.ape create mode 100644 tests/taglib/data/mac-399-id3v2.ape create mode 100644 tests/taglib/data/mac-399-tagged.ape create mode 100644 tests/taglib/data/mac-399.ape create mode 100644 tests/taglib/data/zerodiv.ape create mode 100644 tests/taglib/test_ape.rs diff --git a/tests/taglib/data/longloop.ape b/tests/taglib/data/longloop.ape new file mode 100644 index 0000000000000000000000000000000000000000..3800387acbae60e24fd724f466422521ee53fdf9 GIT binary patch literal 184 zcmeZubXJ(g&%j{9z`!5@#8ZHH4G`Z5Nd}S(0t<{7wrmE2Lz_3R0WuisAzTER^niuo z7$ZBw;+0$sle>5s?t2O_Y?Bve_&clAfSrL03>;tnxaN1m@@;UZhv@5~EW>mK-~7Dn z)JoT41;^5q%zS6Pw1QNiSZ+a4YH@L9ex751Ylx$}Ys3Y11_mXdLtcFO|NlP&g98Ht HkOndU{4PaD literal 0 HcmV?d00001 diff --git a/tests/taglib/data/mac-390-hdr.ape b/tests/taglib/data/mac-390-hdr.ape new file mode 100644 index 0000000000000000000000000000000000000000..c703e2e2fd595469973c4162cfbebcc0722ab829 GIT binary patch literal 128 zcmeZubXKt8U%)TMz{KFPhJitc0Rp&yR9*@L!%cMt1`s>Q)6MOUpcX^8W0-4NZi#{b zP>d0(M_?lp0}F!yLrP*v;trsi8lap{9TP*cE-ORIPIiXhwpPx# literal 0 HcmV?d00001 diff --git a/tests/taglib/data/mac-396.ape b/tests/taglib/data/mac-396.ape new file mode 100644 index 0000000000000000000000000000000000000000..fa7ae4149460cc0a94174a1e3ce72fa4f1106138 GIT binary patch literal 104 zcmeZubXKU~zrZfWz{KFPhJitc0Rk9-)PX7{FbBjA@^o`kdCkcX?il8pmRq7A02E_{ h>Jiw;#K6KJz>tzylE?s569JSH7VO>34%f}V003n#561ui literal 0 HcmV?d00001 diff --git a/tests/taglib/data/mac-399-id3v2.ape b/tests/taglib/data/mac-399-id3v2.ape new file mode 100644 index 0000000000000000000000000000000000000000..2ea97fc45334ff952b0ccf0dacf6d5d11516dc28 GIT binary patch literal 89155 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 Date: Sat, 7 Jan 2023 13:59:34 -0500 Subject: [PATCH 03/75] Tests: Add TagLib APETag tests --- tests/taglib/data/no-tags.ape | Bin 0 -> 158 bytes tests/taglib/main.rs | 1 + tests/taglib/test_apetag.rs | 116 ++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 tests/taglib/data/no-tags.ape create mode 100644 tests/taglib/test_apetag.rs diff --git a/tests/taglib/data/no-tags.ape b/tests/taglib/data/no-tags.ape new file mode 100644 index 0000000000000000000000000000000000000000..2ce41cfe342d70d40304e08ca117d9a0a4d43fe8 GIT binary patch literal 158 zcmeZubXJ(g&%j{9z`!5@#4JGU2gE(UfCLoWe|7#s+$5P+-p^+oE8Z3Vy1)*UP+(*@ z#LWm+A;7@I(6F0 Date: Sun, 8 Jan 2023 15:32:09 -0500 Subject: [PATCH 04/75] Tests: Add TagLib FileRef tests --- tests/taglib/data/blank_video.m4v | Bin 0 -> 15018 bytes tests/taglib/data/click.mpc | Bin 0 -> 1588 bytes tests/taglib/data/click.wv | Bin 0 -> 3176 bytes .../data/correctness_gain_silent_output.opus | Bin 0 -> 35506 bytes tests/taglib/data/empty.ogg | Bin 0 -> 4328 bytes tests/taglib/data/empty.spx | Bin 0 -> 24301 bytes tests/taglib/data/empty.wav | Bin 0 -> 14744 bytes tests/taglib/data/empty_flac.oga | Bin 0 -> 9113 bytes tests/taglib/data/empty_vorbis.oga | Bin 0 -> 4328 bytes tests/taglib/data/has-tags.m4a | Bin 0 -> 5108 bytes tests/taglib/data/no-extension | Bin 0 -> 256 bytes tests/taglib/data/no-tags.3g2 | Bin 0 -> 68335 bytes tests/taglib/data/no-tags.flac | Bin 0 -> 4692 bytes tests/taglib/data/no-tags.m4a | Bin 0 -> 2898 bytes tests/taglib/data/silence-1.wma | Bin 0 -> 35416 bytes tests/taglib/data/unsupported-extension.xx | Bin 0 -> 256 bytes tests/taglib/data/xing.mp3 | Bin 0 -> 8208 bytes tests/taglib/main.rs | 1 + tests/taglib/test_fileref.rs | 287 ++++++++++++++++++ 19 files changed, 288 insertions(+) create mode 100644 tests/taglib/data/blank_video.m4v create mode 100644 tests/taglib/data/click.mpc create mode 100644 tests/taglib/data/click.wv create mode 100644 tests/taglib/data/correctness_gain_silent_output.opus create mode 100644 tests/taglib/data/empty.ogg create mode 100644 tests/taglib/data/empty.spx create mode 100644 tests/taglib/data/empty.wav create mode 100644 tests/taglib/data/empty_flac.oga create mode 100644 tests/taglib/data/empty_vorbis.oga create mode 100644 tests/taglib/data/has-tags.m4a create mode 100644 tests/taglib/data/no-extension create mode 100644 tests/taglib/data/no-tags.3g2 create mode 100644 tests/taglib/data/no-tags.flac create mode 100644 tests/taglib/data/no-tags.m4a create mode 100644 tests/taglib/data/silence-1.wma create mode 100644 tests/taglib/data/unsupported-extension.xx create mode 100644 tests/taglib/data/xing.mp3 create mode 100644 tests/taglib/test_fileref.rs diff --git a/tests/taglib/data/blank_video.m4v b/tests/taglib/data/blank_video.m4v new file mode 100644 index 0000000000000000000000000000000000000000..4bb15ded33f50b99b8887adbc62b2236fa18b860 GIT binary patch literal 15018 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 literal 0 HcmV?d00001 diff --git a/tests/taglib/data/empty.ogg b/tests/taglib/data/empty.ogg new file mode 100644 index 0000000000000000000000000000000000000000..aa533104d6fde177772775137cb7660a3be11418 GIT binary patch literal 4328 zcmeHKeNa9 zMDnm{piDN#)TXHen52-3E3{ba&d$14sftBDD!O&MwWSM)PRCvUsI&j z?jN1b-nqFs_uO;Ox%YR@yZ79Jy1HT%gBHrk8-IL(CKr|8-p+c4^~;wVtE@Jf5tDa} z{zJ$<#A5zWu?py!4+}k$9{Favk>V-;_1&^4HmYek2$WhI>Q@ys)`@pnt2Q(G#rfha zl`31cTBWAdvErh_QvHjYSxAczZWGAYg>1_xOsFqRn-=Luciu_UZ{)R#vNl$XNNsLO zuuJc(knJCHTM#|rRb4Zma_Kk{O1iex^y zj{NNVLaI*Jyew)1*BRZ8c-;j7wN3{sh}WFrQzN=wAtl#vo~6XX9X4 z!ofsz0U=CV#Q%7Y=;Cih7Y9U(IZMjo01U(dIio9R?EKp3!OJ{Srp~+;QH%^SHH(>U zM5wTcYuLkI#LTNfh|`th3YEG-^{!cGCx<$4EC$&TVuKm=i^aL?y?IwkEZ0u%zVTSC z@YSX<(*Z+l!n(aL-l>XBi)Z`*%dqoYs&n=SAln6|q@IL+%@={`uCgS*X+X0)U>s`k zyyf~(GaImtUYrSJjsn#aDC7D;ZZ>ePt1K}}Wel{8`Q-Gf`wPLDn5`GD<5dZ>Zt1?AQzZx;0sZ{=Tz$~Hh9f<+hcsnG0gpjij--uG8Xq)O zPRRj5<0ayqu2z%0ueYZoYeezhWu{qk=h%BmFK{!@VJ|+{e~v8Pojp~$^Bc{q-|_zD z@}2!dq=^W5$wUS}F<`HcPpfek(VDBA#5;k;M0sx%PBHBogbMy48s{y4RR1oz!jl#y zLsd>@Wlt6xEKRl7d`)E^?5numR{8C|&!OIL+E(hTE&EQ5ZXG^qF$8;bBgC-4c3Crr zd$9-G1c#yq{(2_f@(1w^6Zj_+ih_IUf|RvIe8Z&LGM-vl^po91&(}_7R~A2CY1zE{ zCExCflTA0?sJ-#+zAqlDm5Z1yCL^@Qvx4xX6Q1duPCQ>*e?gA4@$?tz`byS0o#Vmt zJyNTuuEp2;rvKKPN8}Mk1YxuYU0mU2S6{QMx4Co9>8XFy*W2fR?o#;9>EpM*uP$Z+ z97kv`zP=G(PvQm=2nJm{mqu6gQ6_F^RE$ihqk9}rSqp%^v^RQaEI0rm?!^=Qk#UhZ zS$dUJT8fodNyF7*%l8%Yq}rSUz$PF!3%RjkrE=Jx(2iG4?0C1UnoHgZ4H)HhLmynq zY!eLqT==TD2>VSRUhUFdRtCq2mHksC`Zj*>aJ4*@Od`Q~Z3y$TZZ?Rtg9@k&G6=cV zOU81So=`7c=;P*CMM13kO6)@aN&d7GYVEyTs0&KK(A`|#-8|kxUvVW?Jsz7|1gFOn zm;bbN0&L~+#&RFC1%`6R{}=mu;>G@7!H*pQ46eGH@Y^ck3kTDYGX*smlko%(`;%Cs zFV5@;|4GgA)aoX?tSa*(EN~dmO^RJ89|p$M&h$q^9vJAdVg`E<2I_?mzafygqL#QY z!{L=@R-2vadAxAKqgjtra54)T-~v~5b04lL$5qQh_URlS0^+{RaNGnX*rVN3itV=S`6cAbFn4g6K**I$&%F{jtnZy_j2hgs>EVxTVO{b z^I)ie57L&aO<&R)z+z~KaHSM2$t;ZUPynA{T zW~S9ORhGrmD~#N}l%=(RrplLmEPBfA(}UbX%QYDH)grp57y;CiM35MFbRF!Fx1SiN z7BAIyOP-6d%Mdyv2=j|Csa8sYm~a@U`1+O7&;%I~2T77hrAkb4=}|AagRo#E(dSLn z%cX}%gs$(8gCs2s|B6yVa>it6k2P`VpOzeS|7X`vtT~%M zUb+=L%CbYapAm!vHm;rsm2kiVdrQjO2lUR&Sv>e@`L6S}QJqY3XsSwa#FnHdi1VAP ziwKG&P0Ev{?WXRd9@5l*wX`F%FE~}+-alN`kx#t;X?c6*vK70`eM8rGygMMM-YHkU zFR1P~&-q2oE@k+$9ZQBeD=HMqtK~aY!-CH%+CwM(?U_Hl{~4*soZ5oWuN<)`ul4oS z$MCmPJL8`c4kjbOO^iX1K+Hf?1j!B!L`9OG(_I2QhLU(Y=;&eIKcoqG4CN;07bwg` zyiL!bpGZW=!Hi-iSt!U~cro<_y);GYVYlEYMot=p4439{y1{uSq79~nys%{=lx#|n zQpqGig!m?o1O|auVoK_LHv0Pl)&AUPK3GSV!|wjE7)>Y3=NQUf_pkC!Br zhSv0uASl?dF4b08?&@vilArXEqv9Fd=gg$M2TQVM_UevUah*usG=!XTt>x~<_ zZAow{>X6;;MktmYw;G+fiN3U9dt=z^pGJ0Z-gal+NBV~5!UT+0%+gl1>Q|wX(k;Qt zE!sC&tLDBs9xGu>I66gAmz^yNG<=lua`V|Go5l!vlNj_Ov;^IS1#Uq+-uUHhHTAn1 z_BOv_r<@&bI3S^~ZU#dgGIM>8t=1ZT6E f2Q~Z5ucPNV{h+3)?Z4Ys(arjy7nGRv!!G{@RvL-o literal 0 HcmV?d00001 diff --git a/tests/taglib/data/empty.spx b/tests/taglib/data/empty.spx new file mode 100644 index 0000000000000000000000000000000000000000..70572b4587f26b1dfa1b93035b65a90f47ffe511 GIT binary patch literal 24301 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 literal 0 HcmV?d00001 diff --git a/tests/taglib/data/empty.wav b/tests/taglib/data/empty.wav new file mode 100644 index 0000000000000000000000000000000000000000..74b5a6de7b02e17ed293c677b873f446f77d84d9 GIT binary patch literal 14744 zcmeIup$&sj07cOU#g`Gf2C@Q@S_J5yG^o}w2qQ2aW56DqtFJoaXBf6Fncq{t{MT2T zq)MIRk?hZ;NtN3=gqOk|2nUC`n|P{Q)8B5O$FcQ6nW*B*QSMf=N6#OLaN^0Wk&6_N z0#ZNi`B0c8zAYvR(9KB@)ou`&2~|a$Ke6cR}l}Q*8&K(hJnlX=r^fHFXbi-lDe7 zKxH?m$2Xx3x2em;(8jxzeim}MsY7mPlb3pW7;;xq=_zP)1$Al!^6aLreTTetl(P>i T_fuYbCLMKbvFzDNznJ+2futKO literal 0 HcmV?d00001 diff --git a/tests/taglib/data/empty_vorbis.oga b/tests/taglib/data/empty_vorbis.oga new file mode 100644 index 0000000000000000000000000000000000000000..aa533104d6fde177772775137cb7660a3be11418 GIT binary patch literal 4328 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 literal 0 HcmV?d00001 diff --git a/tests/taglib/data/has-tags.m4a b/tests/taglib/data/has-tags.m4a new file mode 100644 index 0000000000000000000000000000000000000000..f48a28b520399cdd2184f965f4b690c68f6702d6 GIT binary patch literal 5108 zcmeHJ%WD&15TD(od9`_IYG0vjnzk0Psm2zG2HLbSHKMJQfF6Xp&2DVkY_?>#MMWft z2StQ};z5Lpx1xB~gAk!0cvi3$#G`+JS6OE^8=C|Xgn}OSm&eTf_S>2HW@o+x06KH( zwH0Y)Gztikc}120T3$;6uYR!t6!Hr>p3ibeBhgWSNqE3nqc9zU^g7!G;j*ciMKC+T zoC0$S{)Z_5@Ps8wpuE1 zRo!w=Q5OoRUr|&=FM5@S3;2sl$q{z9#e$CrOpkC(RI0_ZVDb8pu%Zl1v;%ZwkZ%C3 z3oXd7`+PPO4Aq-cR8_Ax>SS-=O(-Y?TfxsmOyfEc2A-lsXW%qHW)1#P4-8>XW-&H_ zdX~oFupy~=gNBa1ot0515YOS#HvpD2njgu|HugUH(R&|ffZzFT3N@`yB z@oX4@OM;Tf>xWEMmLe2TjN~(`MUe(KP~;^MB@i<+J9iG%rFxg#5d(X!;HDmBXYn^jrSGA7JXF!{7&ZL z^E*5GQ%j$kNxq!Cd<(;j81CJ8CupCvui!hAN~eHf7!dFU+73*C(`I+rZBB>7;c_`! zJsn<;+wBPk+Wj4Uz2R`Em*WNwjSUYRj1F;JWPBugJQk0~`-dk}i4&=@lkpfiVO%bk zr_Iyj_4dRDxxv_9L#u$_f%|1GOdqg*#^PtREojG7Dfc?qkq-EpS)j$Vtp-?awAkOo v;-0RR92 literal 0 HcmV?d00001 diff --git a/tests/taglib/data/no-tags.3g2 b/tests/taglib/data/no-tags.3g2 new file mode 100644 index 0000000000000000000000000000000000000000..d31a6ce96d7cb701216e29bf17e15903e3fca5c6 GIT binary patch literal 68335 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%0DOmT!Moqh7w1Y^2~HTN>C$n{#(z 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 AGXMYp literal 0 HcmV?d00001 diff --git a/tests/taglib/data/no-tags.flac b/tests/taglib/data/no-tags.flac new file mode 100644 index 0000000000000000000000000000000000000000..41714416760792085717cb85fa1542e5a7e28981 GIT binary patch literal 4692 zcmeI$&nv@m9LMpGZGLQwubCN}`LSJ$g_Iwqw8?TLq{0=Y9F~=<_~3kMFQA*eisnHwZ)aDHcM}-FQ+$wQ{w8 z>i!PD#`dnCm6iit_fk|SP1zMWvVbfg3&;YpfGi*j$O5u}EFcTW0F)6(kxN;Ls0Gl6>va#5o$UC<*!noNvL3$YV3lvBh-Zt z(v4H`N2oAB84jVM0qW}o()UxrZK&8y`SnnVmwH%+6bm);29;W=)GlNwr|#5H8O|%& znOKC3`P6v_^tUKn9>}ESpYaK*$ev^{YsZk;PK`}Ml@2QDgsNQB#|dO{QlVSO+CxQS vP<1C|dWUSSRP!8E(@ve)pxRC9#|zbMQL}AOeViIR%A_M^yXJC-_V@h)*-@2S literal 0 HcmV?d00001 diff --git a/tests/taglib/data/no-tags.m4a b/tests/taglib/data/no-tags.m4a new file mode 100644 index 0000000000000000000000000000000000000000..ba4e92baef225c0204347186c4aaf42e0259112f GIT binary patch literal 2898 zcmeHJJ#W)s5Psq~5L6^AKGYUEIFx|_Arblmn$m(QRYIyNu^?4WUWZ!sTL-^yD#1`? z?^3DV=~{_Dz<|icFJORynIF(SJZHO2S|L^#{3zDlJ+FOtKHZ%IV5V!|^>lBc2|{FK z5)IToX%*O?E)YlUZV+_*Cy$#8z!DC9w}}@GtR6VCm>-l%S%k6^$|)$P;D49`?(n)S zZZ7b;PLf^PvEJQMvfPKKE8mBwYhO^=JV0&w7Alb67#9rEm+Zyyoqy6Ck~2}Fj2wD{ z!Qd)hjz%{|Bfzyb4Yo2Z2=i_=>MFCy-T1~#M#Q3-j zXKz@zZ)~YJJqu-${?3_R$m&-e?R05)M(2)-g9j?-=TsWXxj<69-cMsc*Q@)+wqxpV zj5S%&a~`kwWtHRvSBN3LlhFi8S*+}8t;WDa7;;OxK>_e4gOINJY-LH7+WAcE93B}kkY(!h0fmY zB-91M`@HotP+g_@P3+Fa-lw0e_c;R@>V0JcYHzz~7*5VS_e2`>dNQWxSc_uArHRIg nO(f_ncc?r}r@-@8>sj&l7xO2r^$l|OCU4jXjrCipjg0*b4AhjE literal 0 HcmV?d00001 diff --git a/tests/taglib/data/silence-1.wma b/tests/taglib/data/silence-1.wma new file mode 100644 index 0000000000000000000000000000000000000000..e06f9176648836ab73928f0e15af92abd5c82f8c GIT binary patch literal 35416 zcmeI*U2Icj7zgnG>1H=(g&i@v5Dd+t1e`W(kcGvMY<7&vhmhfvIB*UrvIw%Q8zV>} zOfZXlh+a4_MsZHvT2QiJNYDfmBXL`#V~Pm|k$@p;Oi*LyoEZJT=bQ#M>y;Pl#s75Y z^z=O+@7v$$#gnJcS!Ye}Y2LFYVZU<*F|0bdZqsBqQx=z&Z}yn2bKH%{fzNw1A%ZBH zyms^S=GpkJ`P~V7&v*DZ5L^~n>un1Ub^q}0lDikQ9y~m=aY5ec+A-5@Qe5{(|*Zq z_v0Y9bXSD8oWFc!Vb9Ex`_7fE*b#Df=2`twJjEA*2%g4WXy&^CR_^>|`u2H0&3~hI zVq{uoRqvGK-qw%(!=Jui(^9$p$=I&)kwtx*lUw*~A>gP05AZAx<9<|mTP+wlKj(6K zZA3ku;By!o*ykE98?hdBTsN%`abAxGtYeGO1s0)*%cd{=dEfR;nAdsi)p-9avF{ru zu~Cx*Bp?9^NI(J-kbndvAOQ(TKmz|up#SYlH#MFp3O~2MyS~a_^XT&bI9v9})f~mO z>v85POwPjMBh-=1AOOL0u`{=9d2Pu@Gj>#ev+qJj{=d#!+BqFtq$sdxm7QGsR5BP?WwA;Qc-l-UH6M=;Y=6k--aoUbltqnO!;Vs4!>>gWe~bOfuN z*&KVbcZ@mOJ%c$56CiJs>2kf^xImV32q4%6tplnRL#daU^*mFI{ zxSAtv;fRBL7pCue<~V{VW7fs2Z{T_f`!3`$w{hK!JILhBDrQ5XxRdL1FxP3R#AfHU z$vhc@h;wWa^EVAVW7O#(?B!p4sIy+mI(zi!H94V1Z?vVa*AFkQN!VR~VQcM*@`9zG ze{%fAx`e&6huN?r7lPhybOo|GTlj3~*UUQ0hpiv-y-wqm&&g#r3~j(99J+&h#B<^~ z7I3n8_?g`mr|jLWc)Y#0qbGSP^Jy^ItKu=4weuU@zQ$(Bl36j!YUL1CI{4KV2f!)Y ze8fz8Yyyz=?C-Y+^Ya6jfBW<0y|(Rd8M*gJTd*}d$ip;AKmrnwfCMBU0SQQ8JO$iH zwwh5gBU$IWUMG@0&F@F^fXv)7{*CAO)VBmAAOQ(TKmrnwfCN$txRGp(*;q!hPHxtT zWDA%po}oxKwV7HaAOQ(TKmrnwfCSP(z>Q@8W;T|QtaHbW6UlC4X~A4YvgvSzGzJMs zKmrnwfCMBUfz$$Cbpc~oMzZFXYqw7JE7r7?zPNi(|b=cH0fW z+GHd<`Ut&}fCMBU0SQPz0uqowItX~x1uTnYB+F&8x?m!(Ns(+iTp^7?0uqpb1SB8< z2}mHdfLC3>vRFp4Tqdgv_+v?JieyupsZ|0JkbndvAOQ(TARPp}>H_{$0wY=H2NT@? zU0?(I6v?K;719_aAOQ(TKmrnwfCN$tc+~|gi)AFs+q{z11vdg+$w*dre5JNis{|w< P0SQPz0uqqGcnbUjXqG|4 literal 0 HcmV?d00001 diff --git a/tests/taglib/data/unsupported-extension.xx b/tests/taglib/data/unsupported-extension.xx new file mode 100644 index 0000000000000000000000000000000000000000..65f57c2ee985713476ac0b6e3483e6fe472e2176 GIT binary patch literal 256 LcmZQz7})>-0RR92 literal 0 HcmV?d00001 diff --git a/tests/taglib/data/xing.mp3 b/tests/taglib/data/xing.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0c880151b6b60b5f5ac4823a064320710b687449 GIT binary patch literal 8208 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 wr}E<+1qcgvl`t<8 literal 0 HcmV?d00001 diff --git a/tests/taglib/main.rs b/tests/taglib/main.rs index 622cbf08..68092e4b 100644 --- a/tests/taglib/main.rs +++ b/tests/taglib/main.rs @@ -3,3 +3,4 @@ pub(crate) mod util; mod test_aiff; mod test_ape; mod test_apetag; +mod test_fileref; diff --git a/tests/taglib/test_fileref.rs b/tests/taglib/test_fileref.rs new file mode 100644 index 00000000..8a0182e0 --- /dev/null +++ b/tests/taglib/test_fileref.rs @@ -0,0 +1,287 @@ +use crate::temp_file; + +use std::fs::File; +use std::io::{Read, Seek}; + +use lofty::error::ErrorKind; +use lofty::resolve::FileResolver; +use lofty::{ + Accessor, AudioFile, FileProperties, FileType, ParseOptions, Tag, TagExt, TagType, TaggedFile, + TaggedFileExt, +}; + +fn file_ref_save(path: &str, expected_file_type: FileType) { + let path = format!("tests/taglib/data/{path}"); + let mut file = temp_file!(path); + { + let mut f = lofty::read_from(&mut file).unwrap(); + file.rewind().unwrap(); + + assert_eq!(f.file_type(), expected_file_type); + + let tag = match f.primary_tag_mut() { + Some(tag) => tag, + None => { + f.insert_tag(Tag::new(f.primary_tag_type())); + f.primary_tag_mut().unwrap() + }, + }; + tag.set_artist(String::from("test artist")); + tag.set_title(String::from("test title")); + tag.set_genre(String::from("Test!")); + tag.set_album(String::from("albummmm")); + tag.set_comment(String::from("a comment")); + tag.set_track(5); + tag.set_year(2020); + tag.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let mut f = lofty::read_from(&mut file).unwrap(); + file.rewind().unwrap(); + + let tag = f.primary_tag_mut().unwrap(); + assert_eq!(tag.artist().as_deref(), Some("test artist")); + assert_eq!(tag.title().as_deref(), Some("test title")); + assert_eq!(tag.genre().as_deref(), Some("Test!")); + assert_eq!(tag.album().as_deref(), Some("albummmm")); + assert_eq!(tag.comment().as_deref(), Some("a comment")); + assert_eq!(tag.track(), Some(5)); + assert_eq!(tag.year(), Some(2020)); + tag.set_artist(String::from("ttest artist")); + tag.set_title(String::from("ytest title")); + tag.set_genre(String::from("uTest!")); + tag.set_album(String::from("ialbummmm")); + tag.set_comment(String::from("another comment")); + tag.set_track(7); + tag.set_year(2080); + tag.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let mut f = lofty::read_from(&mut file).unwrap(); + file.rewind().unwrap(); + + let tag = f.primary_tag_mut().unwrap(); + assert_eq!(tag.artist().as_deref(), Some("ttest artist")); + assert_eq!(tag.title().as_deref(), Some("ytest title")); + assert_eq!(tag.genre().as_deref(), Some("uTest!")); + assert_eq!(tag.album().as_deref(), Some("ialbummmm")); + assert_eq!(tag.comment().as_deref(), Some("another comment")); + assert_eq!(tag.track(), Some(7)); + assert_eq!(tag.year(), Some(2080)); + } + + // NOTE: All tests following this in the TagLib suite are doing the exact same procedures, just + // using their other types: `FileStream` and `ByteVectorStream`. We do not have similar types, + // no need to replicate these. +} + +#[test] +#[ignore] +fn test_musepack() { + // TODO: We don't support MusePack yet + // file_ref_save("click.mpc", FileType::MusePack); +} + +#[test] +#[ignore] +fn test_asf() { + // TODO: We don't support ASF yet + // file_ref_save("silence-1.asf", FileType::ASF); +} + +#[test] +fn test_vorbis() { + file_ref_save("empty.ogg", FileType::Vorbis); +} + +#[test] +fn test_speex() { + file_ref_save("empty.spx", FileType::Speex); +} + +#[test] +fn test_flac() { + file_ref_save("no-tags.flac", FileType::FLAC); +} + +#[test] +fn test_mp3() { + file_ref_save("xing.mp3", FileType::MPEG); +} + +#[test] +#[ignore] +fn test_true_audio() { + // TODO: We don't support TTA yet + // file_ref_save("empty.tta", FileType::TrueAudio); +} + +#[test] +fn test_mp4_1() { + file_ref_save("has-tags.m4a", FileType::MP4); +} + +#[test] +#[ignore] // TODO: The file has a malformed `free` atom. How does TagLib handle this? Currently we mess up entirely and just write a duplicate tag. +fn test_mp4_2() { + file_ref_save("no-tags.m4a", FileType::MP4); +} + +#[test] +#[ignore] // TODO: We are able to write the first tag and even reread, but the second save causes a `SizeMismatch`. +fn test_mp4_3() { + file_ref_save("no-tags.3g2", FileType::MP4); +} + +#[test] +fn test_mp4_4() { + file_ref_save("blank_video.m4v", FileType::MP4); +} + +#[test] +fn test_wav() { + file_ref_save("empty.wav", FileType::WAV); +} + +#[test] +#[ignore] // TODO: We don't yet support FLAC in oga +fn test_oga_flac() { + file_ref_save("empty_flac.oga", FileType::FLAC); +} + +#[test] +fn test_oga_vorbis() { + file_ref_save("empty_vorbis.oga", FileType::Vorbis); +} + +#[test] +fn test_ape() { + file_ref_save("mac-399.ape", FileType::APE); +} + +#[test] +fn test_aiff_1() { + file_ref_save("empty.aiff", FileType::AIFF); +} + +#[test] +fn test_aiff_2() { + file_ref_save("alaw.aifc", FileType::AIFF); +} + +#[test] +fn test_wavpack() { + file_ref_save("click.wv", FileType::WavPack); +} + +#[test] +fn test_opus() { + file_ref_save("correctness_gain_silent_output.opus", FileType::Opus); +} + +#[test] +fn test_unsupported() { + let f1 = lofty::read_from_path("tests/taglib/data/no-extension"); + match f1 { + Err(err) if matches!(err.kind(), ErrorKind::UnknownFormat) => {}, + _ => panic!("File with no extension got through `read_from_path!`"), + } + + let f2 = lofty::read_from_path("tests/taglib/data/unsupported-extension.xx"); + match f2 { + Err(err) if matches!(err.kind(), ErrorKind::UnknownFormat) => {}, + _ => panic!("File with unsupported extension got through `read_from_path!`"), + } +} + +#[test] +#[ignore] +fn test_create() { + // Marker test, Lofty does not replicate this API +} + +#[test] +#[ignore] // TODO: We're off by over 200ms +fn test_audio_properties() { + let file = lofty::read_from_path("tests/taglib/data/xing.mp3").unwrap(); + let properties = file.properties(); + assert_eq!(properties.duration().as_secs(), 2); + assert_eq!(properties.duration().as_millis(), 2064); +} + +#[test] +#[ignore] +fn test_default_file_extensions() { + // Marker test, Lofty does not replicate this API +} + +#[test] +#[ignore] // TODO: We need to check resolvers *first* and then resort to our default implementations +fn test_file_resolver() { + { + let file = lofty::read_from_path("tests/taglib/data/xing.mp3").unwrap(); + assert_eq!(file.file_type(), FileType::MPEG); + } + + struct DummyResolver; + impl Into for DummyResolver { + fn into(self) -> TaggedFile { + TaggedFile::new(FileType::Vorbis, FileProperties::default(), Vec::new()) + } + } + + impl AudioFile for DummyResolver { + type Properties = (); + + fn read_from(_: &mut R, _: ParseOptions) -> lofty::Result + where + R: Read + Seek, + Self: Sized, + { + Ok(Self) + } + + fn save_to(&self, _: &mut File) -> lofty::Result<()> { + todo!() + } + + fn properties(&self) -> &Self::Properties { + todo!() + } + + fn contains_tag(&self) -> bool { + todo!() + } + + fn contains_tag_type(&self, _: TagType) -> bool { + todo!() + } + } + + impl FileResolver for DummyResolver { + fn extension() -> Option<&'static str> { + Some("mp3") + } + + fn primary_tag_type() -> TagType { + todo!() + } + + fn supported_tag_types() -> &'static [TagType] { + todo!() + } + + fn guess(_: &[u8]) -> Option { + Some(FileType::Vorbis) + } + } + + lofty::resolve::register_custom_resolver::("Dummy"); + + { + let file = lofty::read_from_path("tests/taglib/data/xing.mp3").unwrap(); + assert_eq!(file.file_type(), FileType::Vorbis); + } +} From 61f4ab41749b6344ea920961ae5f51459ea29785 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sun, 8 Jan 2023 16:59:33 -0500 Subject: [PATCH 05/75] Tests: Add TagLib FLAC tests --- tests/taglib/data/empty-seektable.flac | Bin 0 -> 4608 bytes tests/taglib/data/multiple-vc.flac | Bin 0 -> 4754 bytes tests/taglib/data/silence-44-s.flac | Bin 0 -> 50904 bytes tests/taglib/data/zero-sized-padding.flac | Bin 0 -> 4692 bytes tests/taglib/main.rs | 1 + tests/taglib/test_flac.rs | 715 ++++++++++++++++++++++ 6 files changed, 716 insertions(+) create mode 100644 tests/taglib/data/empty-seektable.flac create mode 100644 tests/taglib/data/multiple-vc.flac create mode 100644 tests/taglib/data/silence-44-s.flac create mode 100644 tests/taglib/data/zero-sized-padding.flac create mode 100644 tests/taglib/test_flac.rs diff --git a/tests/taglib/data/empty-seektable.flac b/tests/taglib/data/empty-seektable.flac new file mode 100644 index 0000000000000000000000000000000000000000..20dd90d91665a122693ab1150771f7e11b2a136a GIT binary patch literal 4608 zcmYfENpxmlU{DfZ5CBpF3^snE9nA%ds%Am`S618!`pG=WiAO1a?vo!EnSt^w3=A3y zK)NV3Ewv~$FF92qCo{>-$I)5AP|r}$Si#7^z}&#n&>X0Qp%H8%h&2jELtr!nMnhmU z1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz5lo2>kzXP=?_+%!jv&KzzmnF#ZZR5TEHJ zjBhIi;xnIv@%{8be3siV{`BP_KI5ukl7IUW>Rd}qpmU8NH$P&vXZ=4rS)$B?)zqqGKj5Ef2j`C%_OwDPNw3RL# zp0QBu*{U-;cb9a2)(WyRo*B5_yf;%^_;baz+}%aiS*JTbBu0C5DIU?7c;uJb+C)*M zh#t)uE0Q-wc`q^KQr(?5u}?*#Yu2HvGa7UEomj-By3q literal 0 HcmV?d00001 diff --git a/tests/taglib/data/multiple-vc.flac b/tests/taglib/data/multiple-vc.flac new file mode 100644 index 0000000000000000000000000000000000000000..93d9a8a14a2eb60b75cbb1be5c7975a095407b18 GIT binary patch literal 4754 zcmeI$%PWLY9LDi8jLQr&USo`5#&u@nGP#vYk!EC~rrc(MTpAkESO~c-l#s&SWrvl8 zVkZfOg(N#EJ3D28gq86=9=861bL#Z=>381Eclqo01!{#5IVNFJKSm)G{ki8^L`vVbfg3&;Y01;U^0RuQ6mLz<{0X*9Qu7ITpq}xq zrKk%XoVi`d>YzG%pj0Pyl?SD{sMkHnmPZ9IAbSlpI}fE-P?i@cqnIiTL78RLK?any dOnrHw>=kOT6v|nkns&mUQ&xxeWQ}gC`vy%NpK<^I literal 0 HcmV?d00001 diff --git a/tests/taglib/data/silence-44-s.flac b/tests/taglib/data/silence-44-s.flac new file mode 100644 index 0000000000000000000000000000000000000000..24e15deb8c85ef82baf22a231901d646397dd8c4 GIT binary patch literal 50904 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 literal 0 HcmV?d00001 diff --git a/tests/taglib/data/zero-sized-padding.flac b/tests/taglib/data/zero-sized-padding.flac new file mode 100644 index 0000000000000000000000000000000000000000..86ab8bf7b354855713a5352887c4791dbe2310b0 GIT binary patch literal 4692 zcmeI$&nv@m9LMp`Y<_HvubCO<*R}(bkn*EQCW~4jJIwN<(I(Bo33E^jDct?I;pCvW zNg9QNmfWP=+>`?nJDKmRm;b`&U7tQZ`g~5$<2&pPb_*fwCSg*qTp<+0wfjw^Ene=O zbbW_kR(7wRl!kpn`@Bvl)#?|aQIAa#!q?~X_H_8=$O5u}EFcTW0vVbfg z3&;Ypz&|YzQ+EWZr3MaD N$*9$#yV$1vJ-_48A29#` literal 0 HcmV?d00001 diff --git a/tests/taglib/main.rs b/tests/taglib/main.rs index 68092e4b..9cad509d 100644 --- a/tests/taglib/main.rs +++ b/tests/taglib/main.rs @@ -4,3 +4,4 @@ mod test_aiff; mod test_ape; mod test_apetag; mod test_fileref; +mod test_flac; diff --git a/tests/taglib/test_flac.rs b/tests/taglib/test_flac.rs new file mode 100644 index 00000000..49fd4db8 --- /dev/null +++ b/tests/taglib/test_flac.rs @@ -0,0 +1,715 @@ +use crate::temp_file; + +use std::io::{Read, Seek, SeekFrom}; + +use lofty::flac::FlacFile; +use lofty::id3::v2::ID3v2Tag; +use lofty::ogg::{OggPictureStorage, VorbisComments}; +use lofty::{ + Accessor, AudioFile, MimeType, ParseOptions, Picture, PictureInformation, PictureType, TagExt, +}; + +#[test] +fn test_signature() { + let mut file = temp_file!("tests/taglib/data/no-tags.flac"); + let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!( + format!("{:x}", f.properties().signature()), + "a1b141f766e9849ac3db1030a20a3c77" + ); +} + +#[test] +fn test_multiple_comment_blocks() { + let mut file = temp_file!("tests/taglib/data/multiple-vc.flac"); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert_eq!( + f.vorbis_comments().unwrap().artist().as_deref(), + Some("Artist 1") + ); + f.vorbis_comments_mut() + .unwrap() + .set_artist(String::from("The Artist")); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!( + f.vorbis_comments().unwrap().artist().as_deref(), + Some("The Artist") + ); + } +} + +#[test] +fn test_read_picture() { + let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); + let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let lst = f.pictures(); + assert_eq!(lst.len(), 1); + + let (pic, info) = &lst[0]; + assert_eq!(pic.pic_type(), PictureType::CoverFront); + assert_eq!(info.width, 1); + assert_eq!(info.height, 1); + assert_eq!(info.color_depth, 24); + assert_eq!(info.num_colors, 0); + assert_eq!(pic.mime_type(), &MimeType::Png); + assert_eq!(pic.description(), Some("A pixel.")); + assert_eq!(pic.data().len(), 150); +} + +#[test] +fn test_add_picture() { + let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let lst = f.pictures(); + assert_eq!(lst.len(), 1); + + let new_pic = Picture::new_unchecked( + PictureType::CoverBack, + MimeType::Jpeg, + Some(String::from("new image")), + Vec::from("JPEG data"), + ); + let new_pic_info = PictureInformation { + width: 5, + height: 6, + color_depth: 16, + num_colors: 7, + }; + + f.insert_picture(new_pic, Some(new_pic_info)).unwrap(); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let lst = f.pictures(); + assert_eq!(lst.len(), 2); + + let (pic, info) = &lst[0]; + assert_eq!(pic.pic_type(), PictureType::CoverFront); + assert_eq!(info.width, 1); + assert_eq!(info.height, 1); + assert_eq!(info.color_depth, 24); + assert_eq!(info.num_colors, 0); + assert_eq!(pic.mime_type(), &MimeType::Png); + assert_eq!(pic.description(), Some("A pixel.")); + assert_eq!(pic.data().len(), 150); + + let (pic, info) = &lst[1]; + assert_eq!(pic.pic_type(), PictureType::CoverBack); + assert_eq!(info.width, 5); + assert_eq!(info.height, 6); + assert_eq!(info.color_depth, 16); + assert_eq!(info.num_colors, 7); + assert_eq!(pic.mime_type(), &MimeType::Jpeg); + assert_eq!(pic.description(), Some("new image")); + assert_eq!(pic.data(), b"JPEG data"); + } +} + +#[test] +fn test_replace_picture() { + let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let lst = f.pictures(); + assert_eq!(lst.len(), 1); + + let new_pic = Picture::new_unchecked( + PictureType::CoverBack, + MimeType::Jpeg, + Some(String::from("new image")), + Vec::from("JPEG data"), + ); + let new_pic_info = PictureInformation { + width: 5, + height: 6, + color_depth: 16, + num_colors: 7, + }; + + f.remove_pictures(); + f.insert_picture(new_pic, Some(new_pic_info)).unwrap(); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let lst = f.pictures(); + assert_eq!(lst.len(), 1); + + let (pic, info) = &lst[0]; + assert_eq!(pic.pic_type(), PictureType::CoverBack); + assert_eq!(info.width, 5); + assert_eq!(info.height, 6); + assert_eq!(info.color_depth, 16); + assert_eq!(info.num_colors, 7); + assert_eq!(pic.mime_type(), &MimeType::Jpeg); + assert_eq!(pic.description(), Some("new image")); + assert_eq!(pic.data(), b"JPEG data"); + } +} + +#[test] +fn test_remove_all_pictures() { + let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let lst = f.pictures(); + assert_eq!(lst.len(), 1); + + f.remove_pictures(); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let lst = f.pictures(); + assert_eq!(lst.len(), 0); + } +} + +#[test] +#[ignore] // TODO: `VorbisComments::get` is case-sensitive for now, it shouldn't be. +fn test_repeated_save_1() { + let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert_eq!( + f.vorbis_comments().unwrap().title().as_deref(), + Some("Silence") + ); + f.vorbis_comments_mut() + .unwrap() + .set_title(String::from("NEW TITLE")); + f.save_to(&mut file).unwrap(); + + file.rewind().unwrap(); + assert_eq!( + f.vorbis_comments().unwrap().title().as_deref(), + Some("NEW TITLE") + ); + f.vorbis_comments_mut() + .unwrap() + .set_title(String::from("NEW TITLE 2")); + f.save_to(&mut file).unwrap(); + + assert_eq!( + f.vorbis_comments().unwrap().title().as_deref(), + Some("NEW TITLE 2") + ); + } + file.rewind().unwrap(); + { + let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + assert_eq!( + f.vorbis_comments().unwrap().title().as_deref(), + Some("NEW TITLE 2") + ); + } +} + +#[test] +#[ignore] +fn test_repeated_save_2() { + // Marker test, this test relies on saving an ID3v2 tag in a FLAC file, something Lofty does not and will not support. +} + +#[test] +#[ignore] // TODO: We don't make use of padding blocks yet +fn test_repeated_save_3() { + let mut file = temp_file!("tests/taglib/data/no-tags.flac"); + + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut tag = VorbisComments::default(); + tag.set_title(String::from_utf8(vec![b'X'; 8 * 1024]).unwrap()); + f.set_vorbis_comments(tag); + + f.save_to(&mut file).unwrap(); + file.rewind().unwrap(); + assert_eq!(file.metadata().unwrap().len(), 12862); + f.save_to(&mut file).unwrap(); + assert_eq!(file.metadata().unwrap().len(), 12862); +} + +#[test] +fn test_save_multiple_values() { + let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + f.vorbis_comments_mut().unwrap().insert( + String::from("ARTIST"), + String::from("artist 1"), + false, + ); + f.vorbis_comments_mut().unwrap().insert( + String::from("ARTIST"), + String::from("artist 2"), + false, + ); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let mut m = f.vorbis_comments().unwrap().get_all("ARTIST"); + assert_eq!(m.next(), Some("artist 1")); + assert_eq!(m.next(), Some("artist 2")); + assert_eq!(m.next(), None); + } +} + +#[test] +#[ignore] +fn test_dict() { + // Marker test, Lofty does not replicate the dict API +} + +#[test] +fn test_properties() { + let mut tag = VorbisComments::default(); + tag.insert(String::from("ALBUM"), String::from("Album"), false); + tag.insert( + String::from("ALBUMARTIST"), + String::from("Album Artist"), + false, + ); + tag.insert( + String::from("ALBUMARTISTSORT"), + String::from("Album Artist Sort"), + false, + ); + tag.insert(String::from("ALBUMSORT"), String::from("Album Sort"), false); + tag.insert(String::from("ARTIST"), String::from("Artist"), false); + tag.insert(String::from("ARTISTS"), String::from("Artists"), false); + tag.insert( + String::from("ARTISTSORT"), + String::from("Artist Sort"), + false, + ); + tag.insert(String::from("ASIN"), String::from("ASIN"), false); + tag.insert(String::from("BARCODE"), String::from("Barcode"), false); + tag.insert( + String::from("CATALOGNUMBER"), + String::from("Catalog Number 1"), + false, + ); + tag.insert( + String::from("CATALOGNUMBER"), + String::from("Catalog Number 2"), + false, + ); + tag.insert(String::from("COMMENT"), String::from("Comment"), false); + tag.insert(String::from("DATE"), String::from("2021-01-10"), false); + tag.insert(String::from("DISCNUMBER"), String::from("3/5"), false); + tag.insert(String::from("GENRE"), String::from("Genre"), false); + tag.insert(String::from("ISRC"), String::from("UKAAA0500001"), false); + tag.insert(String::from("LABEL"), String::from("Label 1"), false); + tag.insert(String::from("LABEL"), String::from("Label 2"), false); + tag.insert(String::from("MEDIA"), String::from("Media"), false); + tag.insert( + String::from("MUSICBRAINZ_ALBUMARTISTID"), + String::from("MusicBrainz_AlbumartistID"), + false, + ); + tag.insert( + String::from("MUSICBRAINZ_ALBUMID"), + String::from("MusicBrainz_AlbumID"), + false, + ); + tag.insert( + String::from("MUSICBRAINZ_ARTISTID"), + String::from("MusicBrainz_ArtistID"), + false, + ); + tag.insert( + String::from("MUSICBRAINZ_RELEASEGROUPID"), + String::from("MusicBrainz_ReleasegroupID"), + false, + ); + tag.insert( + String::from("MUSICBRAINZ_RELEASETRACKID"), + String::from("MusicBrainz_ReleasetrackID"), + false, + ); + tag.insert( + String::from("MUSICBRAINZ_TRACKID"), + String::from("MusicBrainz_TrackID"), + false, + ); + tag.insert( + String::from("ORIGINALDATE"), + String::from("2021-01-09"), + false, + ); + tag.insert( + String::from("RELEASECOUNTRY"), + String::from("Release Country"), + false, + ); + tag.insert( + String::from("RELEASESTATUS"), + String::from("Release Status"), + false, + ); + tag.insert( + String::from("RELEASETYPE"), + String::from("Release Type"), + false, + ); + tag.insert(String::from("SCRIPT"), String::from("Script"), false); + tag.insert(String::from("TITLE"), String::from("Title"), false); + tag.insert(String::from("TRACKNUMBER"), String::from("2"), false); + tag.insert(String::from("TRACKTOTAL"), String::from("4"), false); + + let mut file = temp_file!("tests/taglib/data/no-tags.flac"); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + + f.set_vorbis_comments(tag.clone()); + + file.rewind().unwrap(); + f.vorbis_comments().unwrap().save_to(&mut file).unwrap(); + } + { + file.rewind().unwrap(); + let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + + assert_eq!(f.vorbis_comments(), Some(&tag)); + } +} + +#[test] +#[ignore] // TODO: Keys are not yet validated +fn test_invalid() { + let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + + f.vorbis_comments_mut().unwrap().insert( + String::from("H\x00c4\x00d6"), + String::from("bla"), + false, + ); +} + +#[test] +fn test_audio_properties() { + let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); + let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + + let properties = f.properties(); + assert_eq!(properties.duration().as_secs(), 3); + // NOTE: This is the number reported by `ffprobe`, TagLib is off here. + assert_eq!(properties.duration().as_millis(), 3684); + // TODO: We report 101, ffprobe reports 110, and TagLib reports 145 + // assert_eq!(properties.audio_bitrate(), 145); + assert_eq!(properties.sample_rate(), 44100); + assert_eq!(properties.channels(), 2); + assert_eq!(properties.bit_depth(), 16); + // TODO + // CPPUNIT_ASSERT_EQUAL(156556ULL, f.audioProperties()->sampleFrames()); + assert_eq!( + format!("{:X}", f.properties().signature()), + "6291DBD8DCB7DC480132E4C4BA154A17" + ); +} + +#[test] +fn test_zero_sized_padding_1() { + let mut file = temp_file!("tests/taglib/data/zero-sized-padding.flac"); + let _ = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); +} + +#[test] +fn test_zero_sized_padding_2() { + let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + f.vorbis_comments_mut() + .unwrap() + .set_title(String::from("ABC")); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + f.vorbis_comments_mut() + .unwrap() + .set_title(String::from_utf8(vec![b'X'; 3067]).unwrap()); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let _ = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + } +} + +#[test] +#[ignore] // TODO: We don't make use of padding blocks yet +fn test_shrink_padding() { + let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + f.vorbis_comments_mut() + .unwrap() + .set_title(String::from_utf8(vec![b'X'; 128 * 1024]).unwrap()); + f.save_to(&mut file).unwrap(); + assert!(file.metadata().unwrap().len() > 128 * 1024); + } + file.rewind().unwrap(); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + f.vorbis_comments_mut() + .unwrap() + .set_title(String::from("0123456789")); + f.save_to(&mut file).unwrap(); + assert!(file.metadata().unwrap().len() < 8 * 1024); + } +} + +#[test] +#[ignore] +fn test_save_id3v1() { + // Marker test, this test relies on saving an ID3v1 tag in a FLAC file, something Lofty does not and will not support. +} + +#[test] +#[ignore] +fn test_update_id3v2() { + // Marker test, this test relies on saving an ID3v2 tag in a FLAC file, something Lofty does not and will not support. +} + +#[test] +fn test_empty_id3v2() { + let mut file = temp_file!("tests/taglib/data/no-tags.flac"); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + f.set_id3v2(ID3v2Tag::default()); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(f.id3v2().is_none()); + } +} + +#[test] +#[ignore] // TODO: TagLib doesn't fully remove Vorbis Comments when stripping. It will preserve the vendor string. Should we do the same? +fn test_strip_tags() { + // NOTE: In the TagLib test suite, this also tests ID3v1 and ID3v2. That is not replicated here. + + let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + f.vorbis_comments_mut() + .unwrap() + .set_title(String::from("XiphComment Title")); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert!(f.vorbis_comments().is_some()); + assert_eq!( + f.vorbis_comments().unwrap().title().as_deref(), + Some("XiphComment Title") + ); + f.remove_vorbis_comments(); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert!(f.vorbis_comments().is_some()); + assert!(f.vorbis_comments().unwrap().is_empty()); + assert_eq!( + f.vorbis_comments().unwrap().vendor(), + "reference libFLAC 1.1.0 20030126" + ); + } +} + +#[test] +fn test_remove_xiph_field() { + let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + f.vorbis_comments_mut() + .unwrap() + .set_title(String::from("XiphComment Title")); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert_eq!( + f.vorbis_comments().unwrap().title().as_deref(), + Some("XiphComment Title") + ); + let _ = f.vorbis_comments_mut().unwrap().remove("TITLE"); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert!(f.vorbis_comments().unwrap().title().is_none()); + } +} + +#[test] +fn test_empty_seek_table() { + let mut file = temp_file!("tests/taglib/data/empty-seektable.flac"); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut tag = VorbisComments::default(); + tag.set_title(String::from("XiphComment Title")); + f.set_vorbis_comments(tag); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.seek(SeekFrom::Start(42)).unwrap(); + assert!(f.vorbis_comments().is_some()); + + let mut data = [0; 4]; + file.read_exact(&mut data).unwrap(); + assert_eq!(data, [3, 0, 0, 0]); + } +} + +#[test] +fn test_picture_stored_after_comment() { + // Blank.png from https://commons.wikimedia.org/wiki/File:Blank.png + const BLANK_PNG_DATA: &[u8] = &[ + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, + 0x52, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x08, 0x06, 0x00, 0x00, 0x00, 0x9D, + 0x74, 0x66, 0x1A, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xAE, 0xCE, 0x1C, + 0xE9, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4D, 0x41, 0x00, 0x00, 0xB1, 0x8F, 0x0B, 0xFC, + 0x61, 0x05, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0E, 0xC3, 0x00, + 0x00, 0x0E, 0xC3, 0x01, 0xC7, 0x6F, 0xA8, 0x64, 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, + 0x54, 0x18, 0x57, 0x63, 0xC0, 0x01, 0x18, 0x18, 0x00, 0x00, 0x1A, 0x00, 0x01, 0x82, 0x92, + 0x4D, 0x60, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82, + ]; + + let mut file = temp_file!("tests/taglib/data/no-tags.flac"); + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert!(f.id3v2().is_none()); + assert!(f.vorbis_comments().is_none()); + assert!(f.pictures().is_empty()); + + let pic = Picture::new_unchecked( + PictureType::CoverFront, + MimeType::Png, + Some(String::from("blank.png")), + BLANK_PNG_DATA.to_vec(), + ); + let pic_information = PictureInformation { + width: 3, + height: 2, + color_depth: 32, + num_colors: 0, + }; + f.insert_picture(pic, Some(pic_information)).unwrap(); + + let mut tag = VorbisComments::default(); + tag.set_title(String::from("Title")); + f.set_vorbis_comments(tag); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert!(f.id3v2().is_none()); + assert!(f.vorbis_comments().is_some()); + + let pictures = f.pictures(); + assert_eq!(pictures.len(), 1); + assert_eq!(pictures[0].0.data(), BLANK_PNG_DATA); + assert_eq!(pictures[0].0.pic_type(), PictureType::CoverFront); + assert_eq!(pictures[0].0.mime_type(), &MimeType::Png); + assert_eq!(pictures[0].0.description(), Some("blank.png")); + assert_eq!(pictures[0].1.width, 3); + assert_eq!(pictures[0].1.height, 2); + assert_eq!(pictures[0].1.color_depth, 32); + assert_eq!(pictures[0].1.num_colors, 0); + assert_eq!( + f.vorbis_comments().unwrap().title().as_deref(), + Some("Title") + ); + } + + const EXPECTED_HEAD_DATA: &[u8] = &[ + b'f', b'L', b'a', b'C', 0x00, 0x00, 0x00, 0x22, 0x12, 0x00, 0x12, 0x00, 0x00, 0x00, 0x0E, + 0x00, 0x00, 0x10, 0x0A, 0xC4, 0x42, 0xF0, 0x00, 0x02, 0x7A, 0xC0, 0xA1, 0xB1, 0x41, 0xF7, + 0x66, 0xE9, 0x84, 0x9A, 0xC3, 0xDB, 0x10, 0x30, 0xA2, 0x0A, 0x3C, 0x77, 0x04, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, b'T', b'I', + b'T', b'L', b'E', b'=', b'T', b'i', b't', b'l', b'e', 0x06, 0x00, 0x00, 0xA9, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x09, b'i', b'm', b'a', b'g', b'e', b'/', b'p', b'n', b'g', + 0x00, 0x00, 0x00, 0x09, b'b', b'l', b'a', b'n', b'k', b'.', b'p', b'n', b'g', 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x77, + ]; + + let mut file_data = Vec::new(); + file.read_to_end(&mut file_data).unwrap(); + + assert!(file_data.starts_with(EXPECTED_HEAD_DATA)); +} From 9c5f1b2cb6b955eca7a19ef671c274ae8ffb2ee1 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Fri, 13 Jan 2023 11:57:56 -0500 Subject: [PATCH 06/75] Tests: Add TagLib FLAC Picture tests --- tests/taglib/main.rs | 1 + tests/taglib/test_flacpicture.rs | 37 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 tests/taglib/test_flacpicture.rs diff --git a/tests/taglib/main.rs b/tests/taglib/main.rs index 9cad509d..4755200c 100644 --- a/tests/taglib/main.rs +++ b/tests/taglib/main.rs @@ -5,3 +5,4 @@ mod test_ape; mod test_apetag; mod test_fileref; mod test_flac; +mod test_flacpicture; diff --git a/tests/taglib/test_flacpicture.rs b/tests/taglib/test_flacpicture.rs new file mode 100644 index 00000000..16826738 --- /dev/null +++ b/tests/taglib/test_flacpicture.rs @@ -0,0 +1,37 @@ +use lofty::{MimeType, Picture, PictureType}; + +const DATA: &[u8] = &[ + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x09, 0x69, 0x6D, 0x61, 0x67, 0x65, 0x2F, 0x70, 0x6E, + 0x67, 0x00, 0x00, 0x00, 0x08, 0x41, 0x20, 0x70, 0x69, 0x78, 0x65, 0x6C, 0x2E, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x96, 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, + 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, + 0x53, 0xDE, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0B, 0x13, 0x00, 0x00, + 0x0B, 0x13, 0x01, 0x00, 0x9A, 0x9C, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4D, 0x45, 0x07, + 0xD6, 0x0B, 0x1C, 0x0A, 0x36, 0x06, 0x08, 0x44, 0x3D, 0x32, 0x00, 0x00, 0x00, 0x1D, 0x74, 0x45, + 0x58, 0x74, 0x43, 0x6F, 0x6D, 0x6D, 0x65, 0x6E, 0x74, 0x00, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x54, 0x68, 0x65, 0x20, 0x47, 0x49, 0x4D, 0x50, 0xEF, + 0x64, 0x25, 0x6E, 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x08, 0xD7, 0x63, 0xF8, 0xFF, + 0xFF, 0x3F, 0x00, 0x05, 0xFE, 0x02, 0xFE, 0xDC, 0xCC, 0x59, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x49, + 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82, +]; + +#[test] +fn test_parse() { + let (picture, info) = Picture::from_flac_bytes(DATA, false).unwrap(); + + assert_eq!(picture.pic_type(), PictureType::CoverFront); + assert_eq!(info.width, 1); + assert_eq!(info.height, 1); + assert_eq!(info.color_depth, 24); + assert_eq!(info.num_colors, 0); + assert_eq!(picture.mime_type(), &MimeType::Png); + assert_eq!(picture.description(), Some("A pixel.")); + assert_eq!(picture.data().len(), 150); +} + +#[test] +fn test_pass_through() { + let (picture, info) = Picture::from_flac_bytes(DATA, false).unwrap(); + assert_eq!(DATA, picture.as_flac_bytes(info, false).as_slice()); +} From 0daf3152ca84b1bc3c533db3a420a54e68615aef Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Fri, 13 Jan 2023 12:21:30 -0500 Subject: [PATCH 07/75] Tests: Add TagLib ID3v1 tests --- tests/taglib/main.rs | 1 + tests/taglib/test_id3v1.rs | 51 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/taglib/test_id3v1.rs diff --git a/tests/taglib/main.rs b/tests/taglib/main.rs index 4755200c..76b9f13e 100644 --- a/tests/taglib/main.rs +++ b/tests/taglib/main.rs @@ -6,3 +6,4 @@ mod test_apetag; mod test_fileref; mod test_flac; mod test_flacpicture; +mod test_id3v1; diff --git a/tests/taglib/test_id3v1.rs b/tests/taglib/test_id3v1.rs new file mode 100644 index 00000000..8cc6bed4 --- /dev/null +++ b/tests/taglib/test_id3v1.rs @@ -0,0 +1,51 @@ +use crate::temp_file; + +use std::io::Seek; + +use lofty::id3::v1::{ID3v1Tag, GENRES}; +use lofty::mpeg::MPEGFile; +use lofty::{Accessor, AudioFile, ParseOptions}; + +#[test] +#[ignore] // TODO: We probably should be stripping whitespace +fn test_strip_whitespace() { + let mut file = temp_file!("tests/taglib/data/xing.mp3"); + { + let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut tag = ID3v1Tag::default(); + tag.set_artist(String::from("Artist ")); + f.set_id3v1(tag); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.id3v1().unwrap().artist().as_deref(), Some("Artist")); + } +} + +#[test] +fn test_genres() { + assert_eq!("Darkwave", GENRES[50]); + assert_eq!( + 100, + GENRES.iter().position(|genre| *genre == "Humour").unwrap() + ); + assert!(GENRES.contains(&"Heavy Metal")); + assert_eq!( + 79, + GENRES + .iter() + .position(|genre| *genre == "Hard Rock") + .unwrap() + ); +} + +#[test] +#[ignore] +fn test_renamed_genres() { + // Marker test, this covers a change where TagLib deviated from the list of genres available on Wikipedia. + // For now, Lofty has no reason to change. +} From f0f5a045c4c9f0a0a5592d02e66ba8fd823774ff Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 14 Jan 2023 20:18:51 -0500 Subject: [PATCH 08/75] Tests: Add TagLib RIFF INFO tests --- tests/taglib/main.rs | 1 + tests/taglib/test_apetag.rs | 2 +- tests/taglib/test_info.rs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/taglib/test_info.rs diff --git a/tests/taglib/main.rs b/tests/taglib/main.rs index 76b9f13e..6b5eb582 100644 --- a/tests/taglib/main.rs +++ b/tests/taglib/main.rs @@ -7,3 +7,4 @@ mod test_fileref; mod test_flac; mod test_flacpicture; mod test_id3v1; +mod test_info; diff --git a/tests/taglib/test_apetag.rs b/tests/taglib/test_apetag.rs index 56d9e142..e8f5d8a0 100644 --- a/tests/taglib/test_apetag.rs +++ b/tests/taglib/test_apetag.rs @@ -78,7 +78,7 @@ fn test_invalid_keys() { let mut tag = ApeTag::default(); tag.insert(valid_space_and_tilde.unwrap()); tag.insert(valid_normal_one.unwrap()); - assert_eq!(tag.items().len(), 2); + assert_eq!(tag.len(), 2); } #[test] diff --git a/tests/taglib/test_info.rs b/tests/taglib/test_info.rs new file mode 100644 index 00000000..f1bce457 --- /dev/null +++ b/tests/taglib/test_info.rs @@ -0,0 +1,30 @@ +use lofty::iff::wav::RIFFInfoList; +use lofty::Accessor; + +#[test] +fn test_title() { + let mut tag = RIFFInfoList::default(); + + assert!(tag.title().is_none()); + tag.set_title(String::from("Test title 1")); + tag.insert(String::from("TEST"), String::from("Dummy Text")); + + assert_eq!(tag.title().as_deref(), Some("Test title 1")); + assert_eq!(tag.get("INAM"), Some("Test title 1")); + assert_eq!(tag.get("TEST"), Some("Dummy Text")); +} + +#[test] +fn test_numeric_fields() { + let mut tag = RIFFInfoList::default(); + + assert!(tag.track().is_none()); + tag.set_track(1234); + assert_eq!(tag.track(), Some(1234)); + assert_eq!(tag.get("IPRT"), Some("1234")); + + assert!(tag.year().is_none()); + tag.set_year(1234); + assert_eq!(tag.year(), Some(1234)); + assert_eq!(tag.get("ICRD"), Some("1234")); +} From 5299ac89a58f8ba94bc9e54617e874168b25e63c Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 14 Jan 2023 20:55:03 -0500 Subject: [PATCH 09/75] Tests: Add TagLib Speex tests --- tests/taglib/main.rs | 1 + tests/taglib/test_speex.rs | 75 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 tests/taglib/test_speex.rs diff --git a/tests/taglib/main.rs b/tests/taglib/main.rs index 6b5eb582..dd456d23 100644 --- a/tests/taglib/main.rs +++ b/tests/taglib/main.rs @@ -8,3 +8,4 @@ mod test_flac; mod test_flacpicture; mod test_id3v1; mod test_info; +mod test_speex; diff --git a/tests/taglib/test_speex.rs b/tests/taglib/test_speex.rs new file mode 100644 index 00000000..dc81bc36 --- /dev/null +++ b/tests/taglib/test_speex.rs @@ -0,0 +1,75 @@ +use crate::temp_file; + +use std::io::Seek; + +use lofty::ogg::{SpeexFile, VorbisComments}; +use lofty::{Accessor, AudioFile, ParseOptions}; + +#[test] +#[ignore] +fn test_audio_properties() { + let mut file = temp_file!("tests/taglib/data/empty.spx"); + let f = SpeexFile::read_from(&mut file, ParseOptions::new()).unwrap(); + + assert_eq!(f.properties().duration().as_secs(), 3); + // TODO: We report 3684, we're off by one + assert_eq!(f.properties().duration().as_millis(), 3685); + // TODO: We report zero, we aren't properly calculating bitrates for Speex + assert_eq!(f.properties().audio_bitrate(), 53); + assert_eq!(f.properties().nominal_bitrate(), -1); + assert_eq!(f.properties().channels(), 2); + assert_eq!(f.properties().sample_rate(), 44100); +} + +#[test] +#[ignore] // TODO: This test doesn't work, it's very specific with file/packet sizes. Have to determine whether or not to even keep this one. +fn test_split_packets() { + let mut file = temp_file!("tests/taglib/data/empty.spx"); + + let text = String::from_utf8(vec![b'X'; 128 * 1024]).unwrap(); + + { + let f = SpeexFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut tag = VorbisComments::default(); + tag.set_title(text.clone()); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let mut f = SpeexFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert_eq!(file.metadata().unwrap().len(), 156330); + assert_eq!(f.vorbis_comments().title().as_deref(), Some(text.as_str())); + + // NOTE: TagLib exposes the packets and page headers through `Speex::File`. + // Lofty does not keep this information around, so we just double check with `ogg_pager`. + let packets = ogg_pager::Packets::read(&mut file).unwrap(); + assert_eq!(packets.get(0).unwrap().len(), 80); + assert_eq!(packets.get(1).unwrap().len(), 131116); + assert_eq!(packets.get(2).unwrap().len(), 93); + assert_eq!(packets.get(3).unwrap().len(), 93); + + assert_eq!(f.properties().duration().as_millis(), 3685); + + f.vorbis_comments_mut().set_title(String::from("ABCDE")); + file.rewind().unwrap(); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = SpeexFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(file.metadata().unwrap().len(), 24317); + assert_eq!(f.vorbis_comments().title().as_deref(), Some("ABCDE")); + + let packets = ogg_pager::Packets::read(&mut file).unwrap(); + assert_eq!(packets.get(0).unwrap().len(), 80); + assert_eq!(packets.get(1).unwrap().len(), 49); + assert_eq!(packets.get(2).unwrap().len(), 93); + assert_eq!(packets.get(3).unwrap().len(), 93); + + assert_eq!(f.properties().duration().as_millis(), 3685); + } +} From 20df29052255177553a131fcd54019a33249ef45 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Fri, 20 Jan 2023 21:15:21 -0500 Subject: [PATCH 10/75] Tests: Add TagLib MPEG tests --- tests/taglib/data/bladeenc.mp3 | Bin 0 -> 28422 bytes tests/taglib/data/duplicate_id3v2.mp3 | Bin 0 -> 10138 bytes tests/taglib/data/garbage.mp3 | Bin 0 -> 8190 bytes tests/taglib/data/invalid-frames1.mp3 | Bin 0 -> 8164 bytes tests/taglib/data/invalid-frames2.mp3 | Bin 0 -> 7898 bytes tests/taglib/data/invalid-frames3.mp3 | Bin 0 -> 8192 bytes tests/taglib/data/lame_cbr.mp3 | Bin 0 -> 4096 bytes tests/taglib/data/lame_vbr.mp3 | Bin 0 -> 4096 bytes tests/taglib/data/mpeg2.mp3 | Bin 0 -> 16384 bytes tests/taglib/data/rare_frames.mp3 | Bin 0 -> 8320 bytes tests/taglib/main.rs | 1 + tests/taglib/test_mpeg.rs | 395 ++++++++++++++++++++++++++ 12 files changed, 396 insertions(+) create mode 100644 tests/taglib/data/bladeenc.mp3 create mode 100644 tests/taglib/data/duplicate_id3v2.mp3 create mode 100644 tests/taglib/data/garbage.mp3 create mode 100644 tests/taglib/data/invalid-frames1.mp3 create mode 100644 tests/taglib/data/invalid-frames2.mp3 create mode 100644 tests/taglib/data/invalid-frames3.mp3 create mode 100644 tests/taglib/data/lame_cbr.mp3 create mode 100644 tests/taglib/data/lame_vbr.mp3 create mode 100644 tests/taglib/data/mpeg2.mp3 create mode 100644 tests/taglib/data/rare_frames.mp3 create mode 100644 tests/taglib/test_mpeg.rs diff --git a/tests/taglib/data/bladeenc.mp3 b/tests/taglib/data/bladeenc.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..e3d1a4b51f46b13dbb62105a896466f71b2d71e6 GIT binary patch literal 28422 zcmY&=c|27A7x%q0V{Bs=L$VV^#xB{n>?vDO+_CSJt%$jnY*E@6BqW3+dq_q}DiujY zqD^)!mJs*3L*L)?JpQOuuetM{_c`x#KIgnYko6;-0KgLBY}0TRmW?%)G1M#s5o9&X z_hxw$^K0KPt~$@u3t6Jw}Y(+?kR_p2?ISOJ1Ws(*)#>EtCg zqYK~gZ*raStk>4R?vtk?~lCb9UVkmdWVPZs%+^cTjZ5tRw=T1Cub0SYwd>+ zWV0Rl|GxkL5LwvzD-;k8CIsm#H;=>2eu+{m4*(qfA3|}NHi~IvcWiH>B9JQq-n)u% z)Ndma+2a&%|M4Keaq+h^bPe=eGkev?WSI6N*{k1wzea92BgoqJqh`?Gsuk9d)!6Ot zw|*T507mo=f$1O+Knr2A1VEd|az^Ro7v01A6f%0wy?q3U^(a$i4uA01Y&R@>$&yK+1gcFX1*_? zP!%msE|HK5pGpl2A}3L=;Psm;|B`WPUa`?h-gi{J*r6fv9-`uXvwj=FQhzOpQ~o2H zA6M#{++Vk;`CQuX^>#7VHS%rqxA1pxL-^^J^Ox@f07yZQx!RLdWJ?v3lo+`s@GZ&q)*OID85>e?j9#mGl4l(?~V}T zyN-wBcZt^$niFWUL?Q+dRENAr-LW3d|90vCL4>j%j$4?MM2ZV!}$5Dn*K(~mVC?J)NQWh0* zva|&b8rSd&dEn+Xq%KVmo#YKT`USRa)C+%!KW+akdl;EQoc3CN(+T{#s{+|{7>sn# z83+&HF!ueKvvTX&82SuX^m7YH*AtZ^se}ED&{0JNUgh}bs_Lt}q1@vkq-X|Z%nyC6 z-Vef9Ch6OgOjmss&e*1O zE01{V*7P_~=md`YPph-0!fl(O82r`#FEjOa<>sT00Xt!#wcfcWllu+I`TeK0#Qw)i zTq$0K%;@go!5S!+L!;~Lf+Z7xK}SD((8tW&s(fDwpjcC00sm>8Xwc*df1-XF3EI$Ft+9| zK0bcOq{j-(?3m^0Vb#aI)KO};6v(PM1Z#*ra9w{^qSbY!47$3FVk=gOL3J`@G2GbkZASQ5el<`4s%32}f1kWwOve4Wk^ z2f$|RDqo#q^(pV>H@YZ@C6Y4}g&-MFTn{|ViGxM?(y=gO2Ti!fw{N(-e9aq>|i4INHHo=jI#6s{Oks7DgO{sw`w0D)DjFqgS>)D>BZrbHc99Sbj( z2#VU=aJAcbU+;J`G&p=sQ@Pg8@c~n?!4sFZ?Z|4x}Q3?4fM7-z( zPO1S7IgB)mDVbSX*$9%u>5wCf-jM@vdm!;R842Kl59Z#1-6Nog{QH^FQ=ZGe+keSD zm^sDe>+Bj0?|U(v7S{CqeEs(WHZ%6_BlooJn*6!@Uz6qjX~VsHVv#( zZ1dK(Bi1WRw)t;MR97zk2Lr;wq7aCZg@NgSm|?=#$MV&^W<`{n-x^ScG9ebw9Fk5n zA|DnPBAXqi!l4cGQ{tETsX4tEL`7S3!M6bFs!jxDtuJL#gd zvOG|);L#pxANxOwwip6f^|l#0zReKyNE%}bnR$trLZUcpvdm2=T{z-{9->7P!+wLo)-N1s?w&tytXDiom}WIOLptRIYIvTT}l5$fzT_l zw+b;Ra>ITLZx65(Nn9jL>^@EwNF0Z_KtG59NW_Dj2Wq+ncNsgH2LFi7D1at%e$2RP zSsSaWIv$n#F;~^qX28dAaK4b!jj5>WgNz`{HA8h~D6OE~-S8L-i|&6~(Sea-guEZe z?NT&g#-8UL)`(&*=J}zoo`{vyA=UE$@tka$q4$CoC-&r9bnr=cL?! z(I;l%G&~)>bLiKF&=b+k*p1i^n_J9mNdyKE2Z2l`IzvnV;dsYZ$idqohdBo085Cht zF5d)$wD$>;T(U4X?i0)hb`!Dpj1$==0eL39lDb#VU7cf4MO)if5cGU=^5L_#dsmH) z?fkx{;_N(<<9E1s^yv$`Ck4Z@?r(I^&l3Hg%R{1=RJfib(6bXJb$Sr3rZVn~z7^~- z2JUxUCG_BFJv1gZu<#>;mnPwD3&-GQzUD@tWAFy0#C3YCdP8b6SNqhWf9ycr_rho1 zs+VObC3R`R!{IvihtSuLpmX)Fu(FD7hnx-e8!8HxZh(P}pN;q}+771s9XiH^9m-5p z8Qdx?3iiPo0H{TKc(CKRo#P*VcQ-m7VV? zFKD=y)|*Xlv$g_`om=a^v;G3!jl^WKRKgUr$lN7fXnlq#kYvJe+d1Tu%)U=I{W7iR z>K#W6_BHG^ys)3R{z@hO!iD=y4G&w*R_@eQ*)RJBA53igR*k-PWXfTV z9%o4am?Y>75db!^9J*{z{R6~*z%$r`9h^+TNj)}Eu8rD6 zHrh9HhS!mU#Xjw$Rg1r)_$&Q`*A)9=+NTEx{A2c3%UhYNR8<>Yx|3e7B2e%jA{HUJ z`n;n$pHjLx^9EU`TL17<(}15}Ts(*31X&iOcH>ScJ=sVY8Ckux07YF4RlmpF&>yZU z@5xcaqE~A=7WPa$-Bm%ON>{Acp003?D53yJjP~fTpv^baZWBD_lmZw{7j1*| zR7gVDLK)EuI4%fZAc&Rl&yJ0?P{^J(@~8}}SoSdN(;f}CbMRL)vR>#fi$uHvvNtK! zd@lyQhtCVk%NobY{VMstECGP<4pjYdwZI-aAQpg)Id7}59@-8$OpySUDdho`S!@Rt z1KSX>&wk#Anmc0}H(u0#osdd~X%PIz2EMGv|&fx2GGyq#xx<0R|Z?dELw% zj+QkPtrd?!_cOQVd@KAvKF@eC7MfybFYC2bw8!SAZLrD{^(SSzbG?^uF3?FrHc^s5 zSdtg0zsgW}f2-R@0h#6OrH7owwrrOIu#E|$`Pn2!y+bCT4}U2LvEV@nqYvmNf-**! zo9WMzy*0x&+L1O&>A{EX<}>ZfJ@db+j~7TBX3KhAyjtok#Zmp<%Ht+Ka&U2JqorzW z;`r|WAOR@}Ie1@&kBUUh@2EHK>QUftE)oOD1!D*T`#2kReNZxIZmwrN(ce3>cV1=P zMs3Yz?sjS8(bV>;m?rHQZ`ssKjZf36vSOm-QA89}3WAt`OE!X1*dK+aTY#8h)~Y;( zYMj;7bi*$%94?LfzK*>9z8NaAIOBpW zydU#K5ZB6xrHZvHho%pCBZsE*n_@QtkQ4fA$oRj|OUKeU2~46;JP8KscuB`KTD06# zNS0WhqJ+~ss4Q{ag(6HCf)O^D_t`F8$}8g8$(HJ`l*)4U7syyksKnXDiicxfjcwDj z%TB!Yzx`VRUMS3|r!=MoUcTLC`5#dX1YU{l^AOA`vzAKVPzS(`xXBT(hx_wdmj*YvuHY()@HOvR=CmA_O< z8S)p2aA;OpLF~RGR;;rt63D?S5zET<>9to+7XH5ObnPE=+=1e8rDqZ|i>Mf@BpnbV zz|yl_hyH~eF>pVe&ZVE2l^T}_b8*473<9tZMjy5WYNiGLgiu*8oI)^bYGH`~hol(( z{>h`1^Va+~(**M5T4ReE1Ji6eogG6%C%!`TfZIeT2sl8JmI$VEz$zVoS<(+-zJB)1 zwN zU>Bb(Xke{yVYW*~+2x1om`ml@5)F^qcD;&;Ep`^Bc(h_%m)Z z!~4VZ06tkmj^9{d3~3h3shy5=skPUzwc_Pk5s%lEs~&9FrSox01f~bsdSD}o8>qu@ zvXFGZP{v*@MK$){$w#poI_x~$z1$=>&>etCtl)dtREG$_kB{XtJ0VZe+*$R^9*HTh z-Q0YyGTSwMwKb|myKVYasx75HI0Fn7YITzvGB9CmjddL}&OSS<8h=W8v63-RkBvPPIzeA2R`s51n+x%?rzCyeRo{8_q#_8Rm5!lihz^^nS z!|;;Kg#!C&*YA_<&%Lw57m?+;`LWP;+m_(G5@)~6B=f+#`S$))&(f=Y>-XO6K2UPo z-<~cFTNG_Ir#P5ZYn!37cckdDjpMpNHQ>s6N#z1@L|8_U7^9CNa*rQE2ZpF` zz6E^Xeb?VvKNuGSV!bk2156s{TJnFpKU1_k{#mAM-Z*2HPe#cTF*jZMbNBn<2o#qN zGS@gnBVl^*dKxqu8fzV-PT=6n&TpL~nMOU@diQYLpq?lR!1RNh9wY{kQJN~{KRAXU ze}8(fEJh&<)i-0(H*|;V$D+06#}2Lg?uzzB_{+0hB?jrjy@f^wa0BQJ;Q>MhzkIc& zMN!0d9TFt5;&`wCm2^dv$B1K9MONfD^Z0d=74A3Yo)n1$x?@9>0qvCDyFR&0QAm{i zW~29D@O=BaFH(n8#i$KLR;iaQnAbTJANKg5Ei-D;XVqTQGQ}G?j%F1?&nhfUh6Z7K zSZQzv%-f^W**!8$VXHNwKBlqhckqdUzX6*)BwoBS0CD)gy{V1NAiBtJ=hwkah*#a% z=IlZVVy;9{8r4ONT{9NPrZS!lL@)iTuUjDpuu7t`y)Ir9UFO*^#e8->Kncgclk^p< znoYVcl5lYBzKWZOJ4aeomtfrRmRoZd%B6cf;LRll_MugJ!X z%heyv$V`b}fwaSeuhp3g#lGL(tFLh#es_MO(a!v8;A~z@(C%Z1U*r%DeGXj;0+MN& z{3LGdV=NsI6HHLu4mtb8tua>bCDL7C_d_CkRNe?1vSwzJ?C8YEkQBgvtf&r_mbhtW zh3j969jmNF+OO%nsa<-LyJ2hfyj^x@c}rM{Zl7j7#V$bUZk43lwdl~hnSZv2k0b6v zDle|8np=M~Wbg1)%el|v)p3k8hKq;8B*6sY^;g4+#e#uqQs=TA>Q`h{7lm(@>|Zo) zFfMD`uX8x)wYEakp2wE9#>k8Q8w0kx=(4ue@+8}c7NzCq;>hRj&*xsD17ZbO-qJ(P zps4wdlqxC;5mUEK=co{_DE|I#Dqq~OyIKL{oXmhXuCT_D&1RR}fpM3Xc5|oq+ykYa zk)JENXYQArP@ZkddvGM4N1^_lP@_w2U9AtM2CdNw;qbU=8R5WdZjcEZ+7frw4O z*`$&l?!wwXOi|U`g(ZYsA|0i(BuA3RpmJe?t-)!H6`Qhl|8#I zTXc{Nt;b5onhyvRkG2y2zH%@+k^yL%IS;j@65u?tfu z18Lx-@uH`pPR|#Z? zKOb~W`Y4HXKx{DEG4#aBWHn{BeFP8a)-@6SWC9JbcHwCukRL-So>mX3fagCRl}kPl z_P4!kJ+@%?Mx=4oE}iK{m$!?$H{az4Ol_t#_94GbuT>AXNZ+|w+vdExxOLy+!FaS; zz-%DMT+IbxPb)#MvV$E-g}u2O(*k!hpHto9e+b@AJej5AJ1L;Zrbaax=WnR`g|ye) zDV|QZDEK(>uuI*_OhE=;y88Z?PjHI7?ZXl6!2j6EngD1rkTqi|Yn&u`Ub}Sg;}#%* z%`Uh!iSI&Soy}qF!SQzA~H~kJWGC7y0Ovj ztn<)l?o_yi|L|nqoa|uiZr8W7F#%5R>jwvNU#{y$JNh^BKZQ}zMv$0B#jk`*b>>48 zF{GD*Uyjd}s+I{#u+ltL#xak2o;^OS@|@Y@O`l{#L(TiwUN3Sl)=tX_n0^Y~<1>=o z+ZB1~-fLak?-2v_a}7~-xmyzwG=Kwg@t~+QWW#7e5ECT-Ij|LSFkuyC2t9(Ip-)bI zEW18xKL|`e&?Q==292{O;o&gI4KIz?>x4*PkML0v!9V*ul3l$z{!zT~rC-ApWNFbs zp*$+G-Q=UOiNQ3oSh?@JGjb>_)^b!^x^$!H0ZLIcT7_y-S*g#&u8HUN)Fg{;=BXm@ zYk@yh--J&Ud<;T9m-3wPVQx#=OdlNb@9;;^*zSCP($sDH-FU(5@lFx<8S?Jn%-yDP zx16$HsS=!j5`N{mdhVzae?%W>(I*Q5RVZD&bU>&m)Cg>K3P;e8Ljgf145cjaB>9n& z*V7!GfPG3{LLC6~h`E#ja)T@x@VM~`#vN%k5957AHz>%`=Gf}uVnp%Y{>6o`$ex!~ zuDu!6&f#+oC5wX7!2v0whhmphcZ8+goVtf**9y8j>Jk)n!vi~-J^5j%d^d+|%w?nosKNi$A?_9UrH#NBQ zt}ATS>OZE|1MVdO<9v`29S|c-C_w?4S`;NGdWN@8+A)~0jDIBekW6~+9}hT9O4?Pp z26ZwgG3a?f<_umqG>PBJo5Hp@SkC?Un|q&jzW(+u~2?#C66nI3rQ;R{ED%?PZYy2tR#|N5{U`eK>}!a zm8VuOu{vT=FYYWoyF7e)bu_VYo~?$Tv@ri6xnSW&)a4GmTdnxOl>S!_r-Y%Q3ui2g zzs^`5qeo#R9S{a!4HQtQHnJ#6Dl0+PDQD^Yb@qS&6D^FfhdT~>tYY&rs9f^h@xzuX zAhpAeqlZcF7R`q#9p9nC@iTr<~yX53dTL zi0FVon7K!c-ue_dp`y_4Vd+Xwwhn>}k5kWrSk z?EFFy8@tt06x}|$f!B5ys*g?mSS38rPB}H*BkjYn*%ImNGE*>)f(S-I^pK1mcN2X6 zzWPd#WxU?6`}XDD{O1we~5T(%Ur%{n!h<^z`c-qVE$grtg2qCzkhYs}8XNtlc7e z>Al?f-Dq=~diGc3+&wd@)y245 zWj%tL7Znrrbg4MFR|9Lwj2SmJA#x4Jd=Rdt~C$gJm6`mU>hLSGZ`V!XgYJxGqp3s06KOX^CWr?mKl zX-{y9p<`xj5iAK=f~ELE4-B?sU5j60;^C(2tLn|wH3j$O4f&Bc}uLpX?G=UrU!~4Pl*9*x9&u4C{T;<}-dQ5r3kY)I?y&lAfrT_8=EMJj3oSkG(mO- z05%0}RK??|Gux z^J8ZYf#V-6R1{!V4Yc(^Lyo>QZvSeB(s&bapSV}^E!T}nqU)=YQQ)MciD==YGdBgj zk~~wdc0(!q!{Bk9a`sc3tHR3?zT&tyOA#NMQkIYJI@QS9XKQK`Be!f)mDa=CINa)F zU178LFM4W4JGr@aj&C*QHA$2 zdzF{|IxL2ZIbCh4+Htz!*?{Kxz&&Yqqa0-r!)Nki4=fzpaO$t=JvRkGkbtXtg`y;R z(3Z{+y20z%Zcf9t0pTGNMP|;r(3FenY@EM2;o~G3hf^qkj>7yN~ zT3Q}ilaCxWcaE~vt-ISa98uKs?UhSgMAM?n{n5wf)hDVwef9^&%GUg%ppT|6*@DR8 zCY&bZ$u2WZXn&$8`Gpu0`C%dfQUC?_fQlsC?xP1g3#*UT&n*eIRw8fw0|TPExvgb+ zgU_cKj$WvwT6}Qrxnf{{J8QhgA=7P;%4eqZjD>!}bEW5#799{eQwZA@g>&0=2*=y= zfloKo(h3*y$;IdbQ({WzTY=YazZ?TzwkzF8=kjVYWyekz*V}pNoxA&$b6rqCex=%y z(ao7O&0N%G#^+?3ZS;+2_q$OqhCpY;U+E2o3IJHNq>i|=3y^9bbK5KB6@!z~wjA=L zEA02B>cFck@wf4}Xaadq*$%xur;0AQOm_A+Z2l|=%{rA}Ww7^8F_g_4!h1-{z~}bu zuw3lb5M{S1)8YqO_nM4SpT2c63;K^KWZk9{2N2A*8S*E3E}>qZM19y4asf-(fDkuf zA?aD`(jY&f2{E`3dc!z@gZv;#ya`zN4yX|@RN*-86d%C%o(I)Kg{~b-FjosgiD_FSn?r z!bXU;<+{yjHgX#;U9rC}HgWEQr?A(>h|2t$+Mu~Wg`&!%T36p2^#;`OQ>`jJwKF6p z`6E#j{96hRJxk0Y9%;TXMkIh5R1e=8Dj8I!GL1W8^$xHDvuvGTt~1@L^l&_PiNb%d z_{7zM;$t}mCx^dfow))R7S(;p^K|COh#u?heZ=VzUGe8ys!o&d4ny_aZ7is1K*sE! z$PH~r+)11#T@#AhxG6cr8%a5xSNyK;OM0K$D=Tt;qLlgaSvMr!@;(nlC7<%MZ7wUV zpFMMHsP^_f-?G%IU3ztd*A-uF4Fx{*d1Ze0<*!*je`i=w_PK3}`a|p4tpOb}*PM_- z@Y<%%>-A4X^PJh;UhgYR) z2Yk-k*M5z9+&yOU`9-{JMWShv_vzZlb0Wu>4dziis4lDrxL%eRrvpOma=hC!lDNbz zhWr=<-Y3I`J8f=#s=H@GjEu{9cii(D(H>%r2Qx24n2I;#XA ziQ}|kBIjG1j!&C68lOJKzqI-Kuv)qNmv?5cw@dGl5VkA4naJ174@0x=zv&ZHu(d4(Xrg83sg^f8A^MyTO*Ri zmS^06bcU{Ox+DS)3vkAPG3jJ03tL@hB>2b@>R^(oJagni_TAb6=hnr8j%I|Xzy_VuaL4JHqn?y95{rQxDTM0?DZoII zgoX1-7(fFH_%PLg5AY0yDa87ie7|W*6B0Gq{xNYw!GB--ihpZJWnlJX0!JASAS)4k zmrz!g6ttSvx}#`&l7$jQ&mn^L_UYj$I`pBJp#V-$3OLS5NMb7#cO$Xk7t4HtzOtK$ z7rH^+_#`aabhF?K;ULWAU0$q5`hyW?O5X}nL2=UUYd^SD`tg@bg`aLS%R*Vy+`Q&s zGl!3$#Yv&I11)bydp6MzcAjp)=)mdSf;;cA>BA~qXW}27myN&0rNPVm7|_Dr<7D&* ziCf#r9MboZPnqJn*24U}rL@_{xBAreBh!W7Ha6F9RI}w<7>^ieEGjbU$ifQ$1s$i% zRt^CyD(KJ$?M=(k!Rb%t2WNb;LO&2YJ$S69&)bcD<+C&f3Q?nnC0?(Groy@)fZ7S( z7zZOU1%`tjUX2p{;^TsD_MI_nE&EPA&@FO0?CIxLFmK)Z2bF5Hl#YY~1YlebS7gH- z58F{eF(Off=|1Y};pGDgNDP1$i4n%gfY(Wkm&Q3AcW};rw=SsPh%G?+gm2{gjzsi- zYm8Vsl4icx7dvvqyXEjYjM+&+I2l-W1YD_=Qz-(I98mrr|aDE0Dlx2(?k zUk2ypmWrMmf0p$zah&$uWl92*MuY6BRcTmPC~ z;Ey8XOKY3g5Z8^IirzD-@8u@nUeooMIrHY>>%MT8GXj+SV*(BdF+r0JC!A+aIDT>V zYo35i9-wGYJJ4J+i~&MyFo!d_(+cfU;+064!>36&%pMXJ-O@0^dg#%Pi4VL^WsJYa z#5<7^5x%%_s|-PY?OyOh=BoR`?NFDGOTKG=)9w#zlstR4hliG8Fokm`jaB|c-F!FN5<@*53`sz@BzBebpQmGK38|htHc;KTZr}2CeQ8uMHYzN( zeQ=ZFt6LDgJ92Z((QYm~c&Nb9IN?9CAVB~a*C)^!LQmiwC_^}_Phs12h)L8Yo_aHm z+{Lv|$QbTOMYmBJacAJ}UFSd`M6?SCoGy}x``zxkI(YZuess|E-Z?C6tgN-K#rop6 zssantTb=Dahd5Mx53HSYz1Q>Td+Be&>l%cPkAmU@37lNlxZ@k7fqN!+rV9BkCfFFY zckp_FB&H-#6zot<_#*+H>~kG=HR(!^Fm8?J+>9*sf2e7V^<>`P_vEP=^11kpP3=zP_-RgJ%q?>@eA8*lKj>2yV zMqaEmBS?thQK8=+wnv3hz&oXeXB6a845C%L}Gpm*# zehth|`wd*NT5D~yUvF-Uag?fDx%~8DM(M{XbLHR$;r0}-iaNwPtN@*)GN>bz#&MgD zctxXPhB6WxMvfG5*qRg?FHPdsD<-kRCM0H<8+;c_*oXOYvz^vH6y{p*?Cqx3(B6JZ z$UpQtc~Oj`WYGuTdp_E->U@Q~Qt80Z8`T{K82TZILT?iwUH}qR+8S*$!mLx<4Eb*Z zQUR3ly?lzijQwY>R;qh3a=QtWoWRFGq1>K0s>;szamh85oaYGowV(gB+TC1r*T&7| z^(Z8FX>2qs&f^A0QGs_AexHb(*=aS)FKSfE%sL+Cc8W}6|+?T`aaIB3>xv3!g?>BmD}@q)NF z&>NgIIPPb_2m^6=;KezmgXkdiJMvLBnPXRdSs*esvAciLd*s@OO6#Rq&!X_Cs4}}? zueHC1iiE@0TP^CpC6mxw929im{mI5aKeOi3xh|?G2YBgXoB%g_Uwn#j{*9g8yPz9* zX6WsAhWItL{SPa&Q=#$A`#mKs_g?UEG9;UGr?$gW~z|qA*{$DOKe&AD;E7XkLRI%aiq86ZziHf7?}! zj=TrwY+HSKoA?0lfQcH9_1|fMM{4;UsO63@0PJl?VwzYIIWNLo=@IEPFQ$;ky9|#$ zW?_P8;h1!q4oITO03l0qYatR{3j2fOVs;AenPdGstzD1sh1s^~Pa9bky)a{OI+kCh z_3O1myo&4xy|1cES5)E`=oD>j2qi%;oG1}Gjg{trU^kyFh5*7R*Dcwm61SRDk?(AZ zm=H)i!VA17ezfZnqtESOYh$=!go|80-GTH`<47Cjl;=se=Fv2b(dTbt`r zzFh0f-?U-kv4tx!JU%~vs-LoNkw>4sRgU-op%Cy3fWw6XFbfv!6uZK9iKULsNK6_0 zgMLrI^#ganA8LlcXfg2pv#Fy>80XR^Qn87w^~H|)t;p-f3P(gWKHD*)J=ztIGXzfx3U1#>&%A>^s8YAv4!$=C_jiUG7`t z!-glu1>>lJiGpsv6GgEESQEkkDG*Pb4y1#VB)=_OBtORqK}K-~kT^hvb^v;)JcvU= zfF*dA`&3a2dqL$g^0vKwu|hk_wH+B7Nl^DYl6E7tD0u8m;+urx2M@lqjxKE&W{ti= zKeYl>cLL~u&^pwNUVT98P|UWBLE=ASMxM5y%Ll0x&F& z0exW~Wg-YCT(RV?-&jXpRR8_8hD5Dyjz;M|TTxiLi)`F!U(R!hT&!L>`S~g(diKAT z?tTidkp?vBWv>AlCi0P&MK6%26WJj%Pz#cTZ$LX>lQWWMhgs%g4-NHj_TN~Gh-}{X zzTwN2cU^ZvC(S=P<)?Zb$BpQeXrca}VIk9jYiG_LGqYLTxfO$({~1aC|LV7zzBBBXHobSduW!0V~aB067Q{N;&Ba-5pI9nyc7&HWIii#w_|y(9=DSo==-5 zUFLliI=e5leSf4?^`W#T7y#tyo1q;5lL7D^=EZQqF1Yt`qMTwWPB1w|h7*j_#7)6h zF(+X=Pz?40%HaSI#*UkOGYsQ7G_X#YHm}}XvT9$PLuO(e5#z0AuB)L z)jU#LwJ2{D-t!O6|7_c%+YE)={I-N%eGme@u%bfHahx;(DkT4rDjiq85qL{dT3FPB zg^6~Lg9w7hV5%Y!3x}R4W%gfxs+*vIyb6ELimYxvdow%uYYnx8AqUi5myy=B!1aTT zwq4$%Ug$H?Nqyre-UyI{xbdY7AV9{edqw9x^iETo;EH2{;+ec~kS1h_L63J1pg+&2 z6S(&rt_w&tviNFjy}uSg%9a;n+x!vp#jdrk*OY|JlFPptci7$#L0`LIOX!}^Lk{&W z^=#)*6v_|^NFZLijcfca`Pc5Q4|aC~gQQJ%!KVa;Ou`;lC=FTaEw`Zww2?vVq0Z$$ zo0TqTaBKzfb-u zJ2BMI7)X-w5*ml)I1&IAh?4=$tp^Yv_%ebjRo1tj{n53(oa^$+=G&F^Fe~9$*PE0> zlwXa_?W3t>k(+aOf37|78aOtNUMoeLioiE^GJ0KgY9a*5t~-6KM_;m-+fx{*-+!3Qe<`D-Vj--+Pa>S;|FuU-iG>!b``o zK(DJ?whlGa)?va1PrFGgCD8$~0c`r)vTa8{;Q847I`$#x5vfZQt}xbQ>_>n_x6(wci{?5~jtC9|56$4?~1>Q>)r(bx@Weu3| zx1x{V@)jjSrp;-)1pb~mSNKCPZKbB{-U87ju{^$TH<1rKT&)vOC;(f23?*5%jk?bK zKJUB{*lKrm)1$kM-}OYq4b!Izb7oST-{sF&-g5mwAH%iNqYRS4cwxsiARl}w8?@*~ z2ZRnu_HP5)8g2X75mhGUqJ9O1DSL^G*Bfz3!9pZn%x_GHABhK)X0_xp;_r*zY=61Y zo$=c8PGx1OUu%SC-;R{->Ps8_t+5MZuiLJU4O|}y)m(W$ReW%2Gx`L*(Wamz)iT5} z5RQNCy_v3X&~8AY+{*l*-D6J4bUuA8T0P54H>^AFi*sI=|AHNC8x+~fmjkEHXI!fM z-Tiu>*`em?ij}LuS=op#nF@iw|z;1aH;%lxcril&tB@MK+UUWt~F2F|D4oW7#M^ClodM@qrsRB)y1`Fm9kZVzxfCHkls%3 zptX%#?TYUP{ZuuZ3&Nj`%u`rTmZ1B=7g*3}v9ltJ7 zW*pmBW2QDXAG}^T6!jt6e{;5N4E}c)&+0e8saR)g9#49)^S9H>->T<#_OpqmAEb>_+S3n7U zIPxDuhSK?$dguRRNNby+dv#LbpP1Y3DkpUaYWY7>!V_sNEcN2Tp05SXrA;lYOSm0P zo~E_xEVK^46#n};VE8G|Dbx=BCVh_YM{ki|)3yAj^$iC43BMiLSbi^00BOf*nH;DG z*Y0um*^J7K@nPawg3hDFj{E!sHh7%Y!`j0Jo~HR=`5)Z-x__jkeNDPxL$y${2{qu} zTCo@qNHdx-EG@qowz87t5q_NVw_iGW%)3qV@bb60sfgKdXUrBPdMXKFq%nmm(3Z*p ztOMHgt^Ox_lp*x`kSV^Dd3=$4UDd0#mBl|md4ed~3E2Q*Ap{Z;?8k&yV{>tz4~ef+ z(7LiwSsWcaIbi5G|EyYkFl&?_8T-3ZWxM{MAT7pY#qq}S20G2cpdvTo=}1aQU*{>B zeC*+C6<1$BnCNS%9#33&tr}ou;BIUAEY6u7;5wIv@hTa(jD7Qm4GdkUzJo z2|w@#G6!H9v$9u)QJu`LD;B40@48f}S3ADDQMMrv-nPT*a@v?hlcJ$e_OgqQ``^nY zeq$Bq?|RezT@BbR1|Man?f^Ryh{n`7_DlCKn(REqUX)^LN!^e66j!*5NQ}#ggO!M^ zKqzCD5bC(uXm{*?i1-y_!sk%G+Vrg{5kq(F%-KK0abWs8=tjuB3OrLOMSi`IOZ<1fVx-_5Y5Tpf) zX5vXg;1$e06Kr!L%FmJng2ojxs_EZ2+oE`+Ah3FACFtDdwT~Ygyp1X-6a6P$UQRpP zEYzt}P>dq<6RX^Qn8ZNjiaQp6PlI?W-K@52PaZMdEZIhvs2J}Xca%uw1`o%{?2X&_ z&(tw#Q#p93(s?O-^V$ru@a~$?W|U*p0(xhya)G>OE_lyWj`gV}+og(8bQ!RnO>{u$ zdMHe6>rNpV9R;J;Sl!&ZTeJu{V18jBY9T_@T=p{+EWxmY|?{j+j z9ocf=BoOBB$s-cJRDI*!!@7(^z58okjLgbc?c3(+ziyjF1{DR=U6w8Wjm{7`URqqs zJ-IZ2mkb?{F4i9Qo!mdcGd}CdMCHW8<<+p4zVb2DRA4=Zgr3VgVdbB{nUPrSXj7eu zjf&jaeB1uT@KDu>SE~vrHCw}ky>&n#p0pdanHGAHe)3)MWA#-#!`IKD7eVsG)nPJ~ zN#dBt7|s*eHhwS$*Q>uawqD!rymw`DX6?PLHBz~J6TM&6;L=(=Vig@|PAIVKKi^<) z>~ZyVO`;|Xy%@zJXfbkg$IBd{GlW`wB)1t#K-VcKLms|Km#iUjqMh3_@p`w$L@me> z6oyz~4G0Hd{CaklOT(HEvxW~>W~c3b7JJjq!u@LAXkchn)pNEWxru4_4sdu~c&Gk= z?xAbhemx(uMh?gQU)44gmmJ!o{kP{Jp7e1{DC1L-Ugue{o?USet(zmm;))gRDYxfh zPQ07%jI7b{F88yv4ttjC!r4cTov&dRWjNZBDjl@o6hbSpnAF zhXTs~eCT&jQ9w<>@$}}DIV^x2?2k>XyT7u_-p1aAmdqTZ++ zN{kt@xUy(Kaamfg9GPCMRP~dv=eR$VbT^W>dPyOFiy>6G-6Zgm4*wXsM`s8Hv@Htm zQ(ZAGkYgh3mMpR87Ln+KIwF)%gMleK;IUx3`S&xMxApMhs@yjJ0Z)R{$0ZG4gTvM5 zI}MTtPPa+U-urTW^^3{%&kM8oDTe|UrrhG+O}7sDIECy*TL?xJTI2J3III_go(9y# zQY!K&AP~{Q4;zHJVc4Y6HWji&7dGhLhD6W{eDq=;Y6ESuK&GdW<>?D;^DhD`0}D2D zB4aK*$mlOcmXuW$HyUp$49)r9`94@gPb4}Z5N1h1tv)D238-BTtwVVzbwN-^DZ2{! zsq`TnG^<`3epc99RIQXS{*?*5B+9OSPIH36uGjm1ojN*&E@?QP-DXz(uDE-k_+eUg z)bU?81TEK)2d0m5!_tOgfAG@)U4OH$z`dgwTQAZlRTbDFZ0@c$Z@}`V#g4NlYHxigl?ps+dd%eav%`-TkL)x}rBl7M{o;Rd- z&Jo7HxNG57ZjdTvnDePlxdWnism{Z6-?&`4XOat(Xq9CK$pgEw*ejA zZhcJ1x4VpWAhkppa=n--`F0{NBnJwcgZg?n#-og#q6?_aV-uwZS!uQ$xfF80^Gmr+ z$fIf7v<N<8EV*8a)D*!EcUY5p~v8Gl+hj zJsN%g=uf-=q|qOsV2WCY{!eS)71q?&MY~f7EkLM-q7)SYK_g9yNa%`m5a|Sz-YgWQ zZUjLo%2CvaC{2nYpddvM5~Td1s0av3w;@$jnt~*E?Zk7w`*0txPd>4~k-6qvbImoz zSeGgMiDU{1tI29k`C&d;&}{?53ko>kfwG-dQ0SG6m7~ZfZqq^ef5%S9h*@B=IML94 z@0vEfO;ro}YfNSZ&0W23Ew;Zfp;(mml^J-r#kh1mux8i7T6Bv2moL`>l7BdGUqDDp zp2+G>Ht6CzU(?^yZmj-t_-6JfR@1^Lk<@wz;S%;gQ{AJ}x@|P=mxL&7X>8}3=a@}d zmwxTeqFvYQ)_E0&FOL1JTrkPMva0vqe6_MzHscm@ec}rKF5ubVxXdZjxILy6rR05H zK}ZhaAzlY|gl<49{cyWtdiYO#+I!|=a)fG~lUL-f zFWe(F-ga3st?ev6>X|L}hNMTMPaDy7eOr%M{{sSIANEn)!Wui=hVgSNHdws^^S~pd+X98eIf>Zt>bTdul{*H8xa?2xl&W1!@b^~ zt_q!UT$@w0S{}GFu89L5ibA4A`G{kcZ5+hw1~yi(54o{>Idh7lskjwoOR|lZgq**Z zAZQhYz(xc?{sa^pYxbd#0>bsNq;kgU_P0ACn+-M=gF-?MQG)}B0fN^Pa@=3c2QCMO zE}nDMsqC22Ir$$jMWF|pMnmE4PFwjV?Fpm#+wsQq&#hFx3{I%0y%q{32*u3=KdT-8 zHoGWV%X74!x{>=ybINh*Yv@6PD&=odf&Bb_=Oom770%%7{(&b5D?lK z=I7XhEID3?Pmi5LvFtGx>?Y`()RGpElq)C>D`DTj{HO*6%n1t70@H)yfDPT$kLt=T zZ+v^{$*f=X92mIAoSwVTxVt2$Lb9ZPZDplKe!Q>oMpRhiZkrb&n=?%O4@SlRi;9Fw zqdQ6AC;8xQ-n;mHmpE5KKMIt{!N(}Ca3vUuFNF&SXN95FHsb=_^O(%ls`{lt>qGX@ zd)L=B2UkM^X2*+T95O-__C8Y$Z)0ZPP4oT7!P*1Sc(H?o00QhXh#jHxNT+Ziyb$<9 znA-wQ0p`OdB8jVtxXVkJ2$8J-g-Qy7wWQB;K6|$_E$vsC#J4I&gOPvrdsmSWPs(z7 z`*~_3v)kR%>V|O~eS$k({{zQIv0UV*MCt+owT+VO6Uxg33@)o zM@^U4HC39|^;qznUsqUNX!u`n(%m8x|Ly6r%>$fQzKNw?Tg#iT@ZY8RZ$R+qr}Sub zfk^_H%R7zBk7$BBA;#mogYHE6#tEiR(G)Bf>Uh#-RDEG`#+488KFmBui{BGJZx`Rv z(skF9akh=ZRT|=wiz+>{b$R9Q_l7JNC7gfZc(r-E{lj_i5mu$6fFBwUWgDVx&hsD< z;(_oHY&(U-OTg>vf^wFEDUQQ#S10zD3Cc#n0!0zPqb5*Xu%8&}^Leajhq12CTGa45 zlR5BgelbqHC9E+h@Y+I!4lsUOJ`{RsV`!X_SL?i~E`(|H=P^Nk>hBqxLgGn9{UGnh za%rc@@JV^63AR(jh#aAO1G@as_o-m%f) zjL7E0N`d^>T^AYkzs|{<#XL%{jo9*dr;$Vv9sr9$vI)!0dlBLP;UMlQc7#&dY8$-t z?Bw(8OE8l=SP2m$k7WUTkj>VYr0}`PoCj}1LzD_fEE44f33NeyJ#^)THiOP!MfFFwlCo zuNi6IJXtjXf~CZu>~L&BmJ%7ot_k>gc7UwdMNXW^U-pE^-zrMPP;9AKNQQ_db8teh zlrbO3Ts)_Yb+tG2!@g|PF>5#O56_o7jK>Xh=Czn43hW>&|{lc#+J-D z&a$#HDEN}$v0!&|+idr^*hRb5iZRpHupj%%chm+~Afyg^1P=tM8=?elun!)H;-K@U zEI=@yC=1YZVrem`39<5=XLtE>o;LACkwr{V3S7P@BB}sV$3iQ{Xo+0gjJdpxwIg%P z`T-wC4q#@Ex9%NUQ`yqAxj8)UD#N&RdB#OQY%5Y1)8%+S{W=mYw*Jh(j$HRPn zSM@aOBrH2j=P~k_^XqcU?|;U=X7pU5{~eMLIUxlu0&IZxLZdnr$h{{{CixMdYW^Lk za1#n75lmv1K5sH18FyQ}Xs0#v()Fz9CdRRrw|ATq;;ZL2b}n5pTMd30wd^cetl?ybHnTz z?bPb@YQDkI1!S}(jda;W4j3ZhE%?Y#>jP}JNo&v82?@TTR_ggwycj(kP}dGZ&lLD3 zbdF|w7r$TAFWBHqU@Q&B-6=JDz8rQhAgEb7(kgueP^hOKC$y~m;JYj4STf46W#toS zw17nf0tiHi$ZqAXvfUJm9fy5ZBJ%?`)nIs^FLJb_jTLthp(&7Zryb-V!PsED|I{vixwfVf>Y@jI5P zpD1|YyM3;0m3EuWp;TepBr<`>i7JrU0vkdS82*emSF1Qjk9;w6Pf&e*c4{bLI?U2$ zbY8xKcII|SjAP8uXFQjirCV5?e<5F zsc0*KfwrPgx9>f8>PNm9_>vZHTRv;&^mZReUc^eO?p;T+8uXHy)SumMp_ut$Q`V@1 zJlrl9@q!w$RT-8vNFEO?acaC=q939W`$paSnZ4BWW{lI9yv@;Z84IGWnEiJ$a^Jfj z@UgpHaS{Ba^t4G@=C|>1H_*?Zm*}P_ z3SAX~$1}A#(F#hxIUZQaAeSsU}T{! zR7}!$EKW*1|7w+IWHw{yW!_KKKfUK_Y0R~Uj9y#5=2>?}qsCb6-iUkKbyNl;m&@xn z`Gmde4+3XZfbQQrW8Miipm4zd1G3y;Jl{42lBm603u&`q7-{5+K56i207({pLK338 zlJNAcd)qGRkiwUpw?5#PV?0`&F>_wi=p6od*<5Qh=*rDo-g!qm3nH}7kc8~8aiQ@W z(cx)Du1`I|Hvv}yTpvn6ofwFp;)}-dLB8G+5-D>RPlpDI<7CKkloHe=M6ue0!9bKC zGz4fKX4X~A!Hqa;ecJ3oJu}QId!BK7ymzCA))%yQyKXR}Vc_tWz0H5g5PxY3y#DMZ zm;M*fCfsIe685Nc0Z9@qLF%(+U%a(_P8kMvirqPwGx3`Vty zOxY|8GIer|Qm)Mg4SknQJ+dTQq|(xKxxa39VAoBDPx^GACR%3#YZ4r!4L`#Y#i6@N z2R0MWaXL4qFWk(+{XWlyC+>i3AV4$ppa}6n1VUsGwqk$F*B8?<%&ohhza3=KeB8P6 z0t0L3nmnzRqJoDj;%M%JyO*uSO5^(nvyda7CrDPL%5gtn4}EU4ivq|kISkl+bE%&V zlC-|wxD>8PJv~X)#u_9(lc5U2T|KsAQzrB11eEvn<@L2j>+ zx=Z>Krla1ru%}k@K%h$@Qpjj|&is9p@7$ByKUy45)G$MNle~>N<*{>!v^7aKl@uyfq|xH2W-4`*^T7p@4i{}Rm^roC9zu~`J?vWZ%th(H5=JtAR{9?(H%YdUT+Ng>Z7$gRe2pM{V0OEx3F9B`4 z`#*aKSO_f+yJ6oXN)WWqD<^WNG6>>{BPn%xsm|JlYeUB>#TCd(wReXI(_$I zX6HBb6vp3|hlLd79C{UyxC9TGjpF^?*)*U@N_7U}>P@%LP--ShJRS6romF>|j z$~hyDQ4Td{y(p&b?(-9-3xojghcgvSM>{4Gk1#OO&Hy@2`e~d6Ke?fmF41k;F*PpC zoB6q;p`9eub~e})>x;@iMj`Xdqv%R6?iz1iViW@-x!a{l`>(~=-gpx?SX}1Or}s0b zjv4mqPNc_BLeP$|l5w*WrQ4tI-rj>8RbxP0LUkahN1Q}p1H!*$tB0J~-8SYE=D`(G z*_Jj7nwxs%mLgu#4U!$(Az}}z21C}t(6OP2LxfTJN-3i>WIf^7#{4XkQBhb`*(BWc zII6GSw9EG7W)U%4hM!!~l8H)y4mDd8w5V8cre?WBT-Hdl`i2oexoTEbFZ#<`lRjY$ zCQeJCl0BrEacClkqQyRg88u za}AZ`0Yr$$TxhBd`C-~^b`J3(3OsN3QaX$#O z#U(!)EN1Rqp8Tpm+_Y>Sn7+VB_|kH5yja}Xb1HRhwq&&~Su$>Q`cX)zC#k#Ii3m_3 zB|9`50R%=0L^hyAw&;^?YcHmwu2sXE_)bH64r7}*Kx*SwtI!%y()ju^d8@ME%@-(o zf@!B`ZR9#(T|Qu~(s(fDrkzA5La7c3Sqtn5FEjtFf`E!+HFL)~sl4#R zL>n>c&LrDZndX~n6Tcr$)Ki1b!abUo;Q|;Iq7u%w!lyaZhbZZ8%!G|I%+JmV8wu`B zHNJmngNtFMf&Jyv<1=&f57st^00J}xMfQ?QVgmxEy;rd0 z^Q2a(MaRk@jYCp>csPsXj3kl@Ng0hne>cNCHlDD~M?7!_XJFH-kD7K~;ql7SGOc+c6S$;>laQ5& zES2a;sSv-P?SoN0L(vkL)Z8Kx+F?6qI?!?{eeiU`q0je>T!uVNf;PGCJ~SB^SbpYE z!oxe^1$+}M&DwZK#_>fZN}rle99|Q5?475+WlV~C+qO+8dZQ8=XUkoD=a{kjP_U!8 zD|4o**KA;Q=UVo0Pwo#FXV(pndi;DnAN+mZ|6XtZK+K_FpZfp-QVxO(Cc$P#(ZTt0 zCqk^zHqaUWH6Jj=vPRp5nv;)ReS_>YOC(xFD2?2c9Qhs;2W$#Qeh#PF1v=|?EsigN#!4zORND)*-IzL80zuJo6vjCBOiK*zuh9DnON_a$a) z2DnHq$V+TJ5cK|jN<)#lS2A8k#*P35T;CT^dP7z@=lb?7Yb}?0B6Iq`+RrRl%?giw z3LVqg55(YA;$bs))8gIl1|e8HK_LjfmV%N|qF8XI@P3f<`mTo?+K)sCkg)1vHv~PL;tRYXU^-_i*PmQa+Cod#ZEHO$TE?H}gjee`ephZd`dam0 zH?l4HI=(t0I&FK%q%5`<@{8#><|z4DCGcc651F;xKqL22-r`MR=&1yv58sUn#;2Ta zKJ9bw)QCb5(E**&+GkICo^q^u&QaWXIf&_5doV0v1r;9VrhWI4^x@Y=Q;lV`igAx*&*K{;*HjopFjZdLU;wxZ37dWBLEO!mu(IWsnnI1X;r68 z0tb)$$Y7$Rl&@P1_WA@rS6HNT)VSB^Bb51l>_3B<-mg8{pZs&S?3%^ss_ESEh}xak zBpnXktqK~u|FxmAo<;{fDGh9_ME}GK{5Ql~?-)?MGJo8U5|XIREI5~?0Zx1*8C3Ul z(cx_`IFI#?WCt;4j?_GP6mm6^XUMKV>2>z})ykf;o75SJ8cF(ZJ=U)nIn`va0>m5+ zzMg`j$Wa_{o%jNNZ@f0{%b(+{2muKmJ3^))hX9~ZtTs-zK#B61*Axezrg-3dIq2P- z2mTGv+Dx3ZU3k;@Z~J8zz3V_vSH*dsr>f48JMlvm3H4KY zJIh`4B2+qlm~^drwrcjbYgLoZj|JFx)`O{&L@A#V-#3u=7R||7*OQ?xd*u;8 z{19G>9id#dNK(!~hBCwJi_4D=l8_knwMfU7(xnLFJvXZ&W(L}HcO4yosWDc8~qBi+2y4_fedp3~JB3`gOX}ISFX(fL|H7dT8&&JLPuu^64bg-*EYNOd6f< zXy($QKS6J!OG8d{(Dd}@?iKF-el=KqnQxg(_kwyCkG)-qQ)z6u zPg}NB)4aL56c`($vnPDXWSiqR!_I&1H=|!ofJSH>X@m|O>oCXvque`<$5SqJyJTBi zI6*1hyrivo6pZVJjzgKYHBkyZDMzYj^&*>Te#4S)?-IP$@U?`qyM;zPn&osIFBe|e zJ~TD`;d+y~wX@>%*b>5SOh91Ovbam&&iDlAwgDh>pyj9n@*$W&&*kdgggW2Wf8=Gw zeTSiaP8NEJ&swvEx_QXmbeI><*&55gg;T7JLPdI`|)a(3!Z^n6kv@ zs{>6~R$ok~_tpf-nH6THjx$Gx+NR7_3tGod(8BI+*O7eric}PPXXd?YZ%N*cYX(BE z02HJ}2(AJi5qNt3zi!)z5*z^9%Lats)EeG4VV_w$V>9%?0k3cH1o4RAfqLk%yxp8A zm|u(K5up!Ss8QO6{xNsWs*SuuK6|@@?^^2WE&rApRoXQ+Ic$~fH^4Cb)cwYAGab|h zWLKqnm27Lx3Jqiu4ft;4-TrW6md`nfs!23Y!G{Wk_v7y7yyhCoSz*>U&&|JET(Hd9 zX60O~Cw+Z%dDj{Dhm}|3&%9Q-YI;2R==vSeY4I<6nA5g;Mu{wFlg6vi`{buV@FlBYln%yk+S!@>2Pzwf&c{)Lu{i$xr`|R@S?n_4wk>jIC*pUZo%97!tf7`4gf;9; z*-rUJ`OagCgJ4|dJMnLHZ(d&I2IT#MBg=go%Gb;o4dx1>F#`tW!`n)#lhXLM(O-k;SIqb(W9n+?r)bRljJEbA%**BlE}E2fF7+|Mv+$E5C;n=?bq|3L`b`bf?g3!(MZ4KbIz z_E1ENu1id4Yl>mS2nj638Uf;)l2xo&_b$+71G>oZ2?JZGI@D~rNJ^ZHQmEukw+=Or zNq1-{|Mwvf-j+>4ekNZW6oU6V_2$Gz4|JwoRVdh5o&uF|^Q$@e45xSJ7FZu>@>t$N z_%DHE0unQc2JbJn5qI;Sp(dE#h~?Kl>%Wg+X!8JX-<+SGga2NBWp{o3y4zUGAnr@- zNP211){^nvlv6eCManCwg}sc5%KyZK`?#d&BUkn9GA!F2K16H0;%N_V?_mkN1R%UI z*oS~}y0o07%f)YL1uSZUMc=2LORYD8M1En;N_H@x&!4li2m}LV^G|hYfSJZ z#(L3wg!y{-(hEZg-GE<%^5y&ZBGj+&amlNpjCoqQIOXV)a6@i5 zgqRP+6sgaLMpeX{WS$x5Hx#-0=!M5=^cRv&cfXC3PF9``JYq9Y%w z>{~&0A6B*7hBBc0QzB6%0x+xf0eW&v~_T53gPg&uX=a4yz zv;9sSpWr#>oZ>HkN4i3PhW2(iVXa?9@DBOwouG~~=a1PvANHt~>$o2tQVhp^`*HZP z_yVbf$Ntu1BOdT52xuK32522d=ucrx3Hr3xn-Z?A4aaVbb($S5qc{;{4<_gHDSTJg z6+5S+ydk+c;mF+lQCj4PHS*MlNq|`q>!lArhoMG^Kh5aZ|ALW1$o)BkJ^W}~9w~`mY$HkBim1^_&>@}il zSK0)TJ5iVFFx^G9Gti{sD9%KPBAo;qam}1G$JrgO-Z?5>8nhY{_2hq!C3?O>?XoFH za}{h3TdP=#{XP+%syFL?FE?#Jc=uxg2Y0sE=H*i-xzdGqJp+bkk)DpV z{8)e>UIk!DvhMAb&h}j*$k{~VVzmVOL}aqG9$FTq=I89u#|ob&s~UacH6U@xLVcwq z|NVFQ{_HVS8T;+i-~QS#RF*PLP0wf+5~;1IEPdE~N#U!;U{jX<;QZ^4)&P?HWzQ7clXoayqBsyx z#&j1VAbZe>-l}-{rOtuS89rY**p2cW=Zla%usFCU3|)heJve8XR=48o%Ig`l-m2xW zLB?gK<(9|M8nnRoi#^9`a=JwHd*65Lo!7}zDg}ii)RQqxDt@j-FY4yTJfJJi;B}0g z==qqZF!twG8-=?D8p>r{$`dj&gjR@5a3f$*8sI0?Z4>iCnj TQ{Lgz37b4<6&cL`>)-zY=z)&) literal 0 HcmV?d00001 diff --git a/tests/taglib/data/duplicate_id3v2.mp3 b/tests/taglib/data/duplicate_id3v2.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..34f4f158ce454d71677f450d1db8c40abbb85d30 GIT binary patch literal 10138 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 YdjCZ_?Wr7v#yEfT4BEkpaZzjfUmgnumH+?% literal 0 HcmV?d00001 diff --git a/tests/taglib/data/invalid-frames1.mp3 b/tests/taglib/data/invalid-frames1.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..c076712c029bb734f1627817660a4b2a4fdf8925 GIT binary patch literal 8164 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 KB$cp(mq{A(q@Lmc literal 0 HcmV?d00001 diff --git a/tests/taglib/data/invalid-frames2.mp3 b/tests/taglib/data/invalid-frames2.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..01976fc54f8da0d764c156a20156998f0b829991 GIT binary patch literal 7898 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 literal 0 HcmV?d00001 diff --git a/tests/taglib/data/invalid-frames3.mp3 b/tests/taglib/data/invalid-frames3.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..6bbd2d39720f4894cbea4358e5b85d6507ce0a0b GIT binary patch literal 8192 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 literal 0 HcmV?d00001 diff --git a/tests/taglib/data/lame_vbr.mp3 b/tests/taglib/data/lame_vbr.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..643056ef837c7f35db265db857814aa2c5ecda8b GIT binary patch literal 4096 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 AumAu6 literal 0 HcmV?d00001 diff --git a/tests/taglib/data/rare_frames.mp3 b/tests/taglib/data/rare_frames.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..e485337f9e489dea7fecc899c7418cadc923c4b4 GIT binary patch literal 8320 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 N691C_K>+}&{{TD^OUM8K literal 0 HcmV?d00001 diff --git a/tests/taglib/main.rs b/tests/taglib/main.rs index dd456d23..424ee197 100644 --- a/tests/taglib/main.rs +++ b/tests/taglib/main.rs @@ -8,4 +8,5 @@ mod test_flac; mod test_flacpicture; mod test_id3v1; mod test_info; +mod test_mpeg; mod test_speex; diff --git a/tests/taglib/test_mpeg.rs b/tests/taglib/test_mpeg.rs new file mode 100644 index 00000000..42435717 --- /dev/null +++ b/tests/taglib/test_mpeg.rs @@ -0,0 +1,395 @@ +use crate::temp_file; + +use std::fs::File; +use std::io::Seek; + +use lofty::ape::ApeTag; +use lofty::id3::v1::ID3v1Tag; +use lofty::id3::v2::{ID3v2Tag, ID3v2Version}; +use lofty::mpeg::MPEGFile; +use lofty::{Accessor, AudioFile, ParseOptions}; + +#[test] +#[ignore] +fn test_audio_properties_xing_header_cbr() { + let mut file = File::open("tests/taglib/data/lame_cbr.mp3").unwrap(); + let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + + assert_eq!(f.properties().duration().as_secs(), 1887); // TODO: Off by 9 + assert_eq!(f.properties().duration().as_millis(), 1887164); + assert_eq!(f.properties().audio_bitrate(), 64); + assert_eq!(f.properties().channels(), 1); + assert_eq!(f.properties().sample_rate(), 44100); + // TODO? + // CPPUNIT_ASSERT_EQUAL(MPEG::XingHeader::Xing, f.audioProperties()->xingHeader()->type()); +} + +#[test] +#[ignore] +fn test_audio_properties_xing_header_vbr() { + let mut file = File::open("tests/taglib/data/lame_vbr.mp3").unwrap(); + let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + + assert_eq!(f.properties().duration().as_secs(), 1887); // TODO: Off by 9 + assert_eq!(f.properties().duration().as_millis(), 1887164); + assert_eq!(f.properties().audio_bitrate(), 70); + assert_eq!(f.properties().channels(), 1); + assert_eq!(f.properties().sample_rate(), 44100); + // TODO? + // CPPUNIT_ASSERT_EQUAL(MPEG::XingHeader::Xing, f.audioProperties()->xingHeader()->type()); +} + +#[test] +#[ignore] +fn test_audio_properties_vbri_header() { + let mut file = File::open("tests/taglib/data/rare_frames.mp3").unwrap(); + let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + + assert_eq!(f.properties().duration().as_secs(), 222); // TODO: Off by 1 + assert_eq!(f.properties().duration().as_millis(), 222198); + assert_eq!(f.properties().audio_bitrate(), 233); + assert_eq!(f.properties().channels(), 2); + assert_eq!(f.properties().sample_rate(), 44100); + // TODO? + // CPPUNIT_ASSERT_EQUAL(MPEG::XingHeader::VBRI, f.audioProperties()->xingHeader()->type()); +} + +#[test] +#[ignore] +fn test_audio_properties_no_vbr_headers() { + let mut file = File::open("tests/taglib/data/bladeenc.mp3").unwrap(); + let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + + assert_eq!(f.properties().duration().as_secs(), 3); // Off by 1 + assert_eq!(f.properties().duration().as_millis(), 3553); + assert_eq!(f.properties().audio_bitrate(), 64); + assert_eq!(f.properties().channels(), 1); + assert_eq!(f.properties().sample_rate(), 44100); + + // NOTE: This test also checks the last frame of the file. That information is not saved + // in Lofty, and it doesn't seem too useful to expose. +} + +#[test] +fn test_skip_invalid_frames_1() { + let mut file = File::open("tests/taglib/data/invalid-frames1.mp3").unwrap(); + let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + + assert_eq!(f.properties().duration().as_secs(), 0); + assert_eq!(f.properties().duration().as_millis(), 392); + assert_eq!(f.properties().audio_bitrate(), 160); + assert_eq!(f.properties().channels(), 2); + assert_eq!(f.properties().sample_rate(), 44100); +} + +#[test] +#[ignore] +fn test_skip_invalid_frames_2() { + let mut file = File::open("tests/taglib/data/invalid-frames2.mp3").unwrap(); + let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + + assert_eq!(f.properties().duration().as_secs(), 0); + assert_eq!(f.properties().duration().as_millis(), 314); // TODO: Off by 79 + assert_eq!(f.properties().audio_bitrate(), 192); + assert_eq!(f.properties().channels(), 2); + assert_eq!(f.properties().sample_rate(), 44100); +} + +#[test] +#[ignore] +fn test_skip_invalid_frames_3() { + let mut file = File::open("tests/taglib/data/invalid-frames3.mp3").unwrap(); + let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + + assert_eq!(f.properties().duration().as_secs(), 0); + assert_eq!(f.properties().duration().as_millis(), 183); // TODO: Off by 26 + assert_eq!(f.properties().audio_bitrate(), 320); + assert_eq!(f.properties().channels(), 2); + assert_eq!(f.properties().sample_rate(), 44100); +} + +#[test] +#[ignore] +fn test_version_2_duration_with_xing_header() { + let mut file = File::open("tests/taglib/data/mpeg2.mp3").unwrap(); + let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.properties().duration().as_secs(), 5387); // TODO: Off by 15 + assert_eq!(f.properties().duration().as_millis(), 5387285); +} + +#[test] +fn test_save_id3v24() { + let mut file = temp_file!("tests/taglib/data/xing.mp3"); + + let xxx = "X".repeat(254); + { + let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + assert!(f.id3v2().is_none()); + + let mut tag = ID3v2Tag::default(); + tag.set_title(xxx.clone()); + tag.set_artist(String::from("Artist A")); + f.set_id3v2(tag); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.id3v2().unwrap().original_version(), ID3v2Version::V4); + assert_eq!(f.id3v2().unwrap().artist().as_deref(), Some("Artist A")); + assert_eq!(f.id3v2().unwrap().title().as_deref(), Some(xxx.as_str())); + } +} + +#[test] +#[ignore] +fn test_save_id3v24_wrong_param() { + // Marker test, Lofty does not replicate the TagLib saving API +} + +#[test] +#[ignore] // TODO: We don't yet support writing an ID3v23 tag (#62) +fn test_save_id3v23() { + let mut file = temp_file!("tests/taglib/data/xing.mp3"); + + let xxx = "X".repeat(254); + { + let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + assert!(f.id3v2().is_none()); + + let mut tag = ID3v2Tag::default(); + tag.set_title(xxx.clone()); + tag.set_artist(String::from("Artist A")); + f.set_id3v2(tag); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.id3v2().unwrap().original_version(), ID3v2Version::V3); + assert_eq!(f.id3v2().unwrap().artist().as_deref(), Some("Artist A")); + assert_eq!(f.id3v2().unwrap().title().as_deref(), Some(xxx.as_str())); + } +} + +#[test] +fn test_duplicate_id3v2() { + let mut file = File::open("tests/taglib/data/duplicate_id3v2.mp3").unwrap(); + let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.properties().sample_rate(), 44100); +} + +#[test] +fn test_fuzzed_file() { + let mut file = File::open("tests/taglib/data/invalid-frames3.mp3").unwrap(); + let _ = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); +} + +#[test] +#[ignore] +fn test_frame_offset() { + // Marker test, Lofty does not replicate this API. Doesn't seem useful to retain frame offsets. +} + +#[test] +fn test_strip_and_properties() { + let mut file = temp_file!("tests/taglib/data/xing.mp3"); + + { + let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut id3v2 = ID3v2Tag::default(); + id3v2.set_title(String::from("ID3v2")); + f.set_id3v2(id3v2); + let mut ape = ApeTag::default(); + ape.set_title(String::from("APE")); + f.set_ape(ape); + let mut id3v1 = ID3v1Tag::default(); + id3v1.set_title(String::from("ID3v1")); + f.set_id3v1(id3v1); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.id3v2().unwrap().title().as_deref(), Some("ID3v2")); + f.remove_id3v2(); + assert_eq!(f.ape().unwrap().title().as_deref(), Some("APE")); + f.remove_ape(); + assert_eq!(f.id3v1().unwrap().title().as_deref(), Some("ID3v1")); + f.remove_id3v1(); + assert!(!f.contains_tag()); + } +} + +#[test] +fn test_properties() {} + +#[test] +#[ignore] +fn test_repeated_save_1() { + // Marker test, yet another case of checking frame offsets that Lofty does not expose. +} + +#[test] +#[ignore] +fn test_repeated_save_2() { + // Marker test, not entirely sure what's even being tested here? +} + +#[test] +fn test_repeated_save_3() { + let mut file = temp_file!("tests/taglib/data/xing.mp3"); + + { + let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + assert!(f.ape().is_none()); + assert!(f.id3v1().is_none()); + + { + let mut ape = ApeTag::default(); + ape.set_title(String::from("01234 56789 ABCDE FGHIJ")); + f.set_ape(ape); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + f.ape_mut().unwrap().set_title(String::from("0")); + f.save_to(&mut file).unwrap(); + } + { + let mut id3v1 = ID3v1Tag::default(); + id3v1.set_title(String::from("01234 56789 ABCDE FGHIJ")); + f.set_id3v1(id3v1); + } + file.rewind().unwrap(); + { + f.ape_mut().unwrap().set_title(String::from( + "01234 56789 ABCDE FGHIJ 01234 56789 ABCDE FGHIJ 01234 56789", + )); + f.save_to(&mut file).unwrap(); + } + } + file.rewind().unwrap(); + { + let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(f.ape().is_some()); + assert!(f.id3v1().is_some()); + } +} + +#[test] +fn test_empty_id3v2() { + let mut file = temp_file!("tests/taglib/data/xing.mp3"); + + { + let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut id3v2 = ID3v2Tag::default(); + id3v2.set_title(String::from("0123456789")); + f.set_id3v2(id3v2); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut id3v2 = ID3v2Tag::default(); + id3v2.set_title(String::new()); + f.set_id3v2(id3v2); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(f.id3v2().is_none()); + } +} + +#[test] +fn test_empty_id3v1() { + let mut file = temp_file!("tests/taglib/data/xing.mp3"); + + { + let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut id3v1 = ID3v1Tag::default(); + id3v1.set_title(String::from("0123456789")); + f.set_id3v1(id3v1); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut id3v1 = ID3v1Tag::default(); + id3v1.set_title(String::new()); + f.set_id3v1(id3v1); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(f.id3v1().is_none()); + } +} + +#[test] +fn test_empty_ape() { + let mut file = temp_file!("tests/taglib/data/xing.mp3"); + + { + let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut ape = ApeTag::default(); + ape.set_title(String::from("0123456789")); + f.set_ape(ape); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut ape = ApeTag::default(); + ape.set_title(String::new()); + f.set_ape(ape); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(f.ape().is_none()); + } +} + +#[test] +#[ignore] // TODO: We can't find an ID3v2 tag after saving with garbage +fn test_ignore_garbage() { + let mut file = temp_file!("tests/taglib/data/garbage.mp3"); + + { + let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + assert!(f.id3v2().is_some()); + + assert_eq!(f.id3v2().unwrap().title().as_deref(), Some("Title A")); + f.id3v2_mut().unwrap().set_title(String::from("Title B")); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(f.id3v2().is_some()); + assert_eq!(f.id3v2().unwrap().title().as_deref(), Some("Title B")); + } +} From 5822a3d19fbcaa290f4756af9ada5281ce098efd Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Tue, 28 Feb 2023 22:37:04 -0500 Subject: [PATCH 11/75] Tests: Update TagLib MPEG tests for new behavior --- tests/taglib/test_mpeg.rs | 84 +++------------------------------------ 1 file changed, 6 insertions(+), 78 deletions(-) diff --git a/tests/taglib/test_mpeg.rs b/tests/taglib/test_mpeg.rs index 42435717..e88c47a6 100644 --- a/tests/taglib/test_mpeg.rs +++ b/tests/taglib/test_mpeg.rs @@ -283,93 +283,21 @@ fn test_repeated_save_3() { } #[test] +#[ignore] fn test_empty_id3v2() { - let mut file = temp_file!("tests/taglib/data/xing.mp3"); - - { - let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); - file.rewind().unwrap(); - - let mut id3v2 = ID3v2Tag::default(); - id3v2.set_title(String::from("0123456789")); - f.set_id3v2(id3v2); - f.save_to(&mut file).unwrap(); - } - file.rewind().unwrap(); - { - let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); - file.rewind().unwrap(); - - let mut id3v2 = ID3v2Tag::default(); - id3v2.set_title(String::new()); - f.set_id3v2(id3v2); - f.save_to(&mut file).unwrap(); - } - file.rewind().unwrap(); - { - let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); - assert!(f.id3v2().is_none()); - } + // Marker test, Lofty accepts empty strings as valid values } #[test] +#[ignore] fn test_empty_id3v1() { - let mut file = temp_file!("tests/taglib/data/xing.mp3"); - - { - let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); - file.rewind().unwrap(); - - let mut id3v1 = ID3v1Tag::default(); - id3v1.set_title(String::from("0123456789")); - f.set_id3v1(id3v1); - f.save_to(&mut file).unwrap(); - } - file.rewind().unwrap(); - { - let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); - file.rewind().unwrap(); - - let mut id3v1 = ID3v1Tag::default(); - id3v1.set_title(String::new()); - f.set_id3v1(id3v1); - f.save_to(&mut file).unwrap(); - } - file.rewind().unwrap(); - { - let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); - assert!(f.id3v1().is_none()); - } + // Marker test, Lofty accepts empty strings as valid values } #[test] +#[ignore] fn test_empty_ape() { - let mut file = temp_file!("tests/taglib/data/xing.mp3"); - - { - let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); - file.rewind().unwrap(); - - let mut ape = ApeTag::default(); - ape.set_title(String::from("0123456789")); - f.set_ape(ape); - f.save_to(&mut file).unwrap(); - } - file.rewind().unwrap(); - { - let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); - file.rewind().unwrap(); - - let mut ape = ApeTag::default(); - ape.set_title(String::new()); - f.set_ape(ape); - f.save_to(&mut file).unwrap(); - } - file.rewind().unwrap(); - { - let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); - assert!(f.ape().is_none()); - } + // Marker test, Lofty accepts empty strings as valid values } #[test] From 1e44f90aee458aa0b5cca68d3e01db59c496f0fb Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Fri, 10 Mar 2023 17:54:12 -0500 Subject: [PATCH 12/75] Tests: Add TagLib MP4 tests --- tests/taglib/data/covr-junk.m4a | Bin 0 -> 5108 bytes tests/taglib/data/empty_alac.m4a | Bin 0 -> 5380 bytes tests/taglib/data/gnre.m4a | Bin 0 -> 5026 bytes tests/taglib/data/ilst-is-last.m4a | Bin 0 -> 32768 bytes tests/taglib/data/infloop.m4a | Bin 0 -> 53192 bytes tests/taglib/data/non-full-meta.m4a | Bin 0 -> 5108 bytes tests/taglib/main.rs | 1 + tests/taglib/test_mp4.rs | 482 ++++++++++++++++++++++++++++ 8 files changed, 483 insertions(+) create mode 100644 tests/taglib/data/covr-junk.m4a create mode 100644 tests/taglib/data/empty_alac.m4a create mode 100644 tests/taglib/data/gnre.m4a create mode 100644 tests/taglib/data/ilst-is-last.m4a create mode 100644 tests/taglib/data/infloop.m4a create mode 100644 tests/taglib/data/non-full-meta.m4a create mode 100644 tests/taglib/test_mp4.rs diff --git a/tests/taglib/data/covr-junk.m4a b/tests/taglib/data/covr-junk.m4a new file mode 100644 index 0000000000000000000000000000000000000000..ac80cb29d2585e9d728a86b5027973690f51d174 GIT binary patch literal 5108 zcmeHK-)qxw5Wgf{e{Jnj?c7gBwr+EX>$Gmjbg*@+oe|w+6ciC=)}*ekX-1O`6_MhT zBErBui7@d&7>dvOAXFF#J}Wo};-i0nuM+Rl)U8DXVW1EBw8`D~^8Mc3_ru*cEdaD+ zj2lbp(ohT#B(u7v0@S@$Wy$#B0?1|OGm?}Rjz(ic02A;~aK&J%3&d5n6(U7PaY%3~ z!RZ303;u^L00>4@Oh~~@2qLmBtE0;&^7V@oR5oF<*bA* z1XZrtFT6+qd%I-2?^=*^g-R%0Th3Q>TXOHzY(9enURTSG><2w^+1?=+6naje6dRTc zIiaj`_jO}Fhx%3B&@Ix-cDR6VR7#Gpvn>{QGGKayV~SoTk3;gh(cnTE9&Z9@!yw-P z>gVgw!tUeUC>Y{f&<(@l8+975<02Fkf-T{-hH2a+gN-LB(HZ!h%i-W3)j&ViWEx`= zsAj1h4%?E7x5L)4w$mEw1hNY_^$h?g;_xGl)77<)eyrNZ9w7K4%d#P%Y*!VM7OHw5 z&x?Yzv_$P%0lSvX=?0FT4j@o=sETAA5NS<~(qtu?O)cjYYPxVD|C#_*%5;j@!t9Aser%Dk7__XotQ;Q6tiBs|M>8T(mo(2 z;;HM8)3?^&mk^tDD*=Rj%*E$-w=KrSQ`3o;6IbqFm_gfp09S(f$@~iZnWUHmhG9U) z518992|l;S>v8+MUa#Nps}D9e1_Oa$N4P1}+}#<8bae_s@1fy=-h;7zL5PkG#*UAS zj*j*WOeEtclEWuQN5~1|_xpnl!S=?+_K`lJZ{)9GmLTNC{jwa>4Qz zz6%~ShxuA7&}7jreQ|_4eI$ yky3O+h!>2kRcc)uhDNNn?w*p2$0zN7TJB4kf}&tz4jcv?1{?+)1{?STA5Zo9L)-I)aHLE_1O zL%c}AqemeMLPh)|?4RJxLt%YyKiqAsB2-XeUNU)a-g`5@dB1&|F92X&wHs`-t}s9$ zErQm%BYi=0fO1v%{s0YE3^pzDd^}s)$MFH+)m1Q_w4ns_%;m@g-DC51o_U;Q02o^; zyie#rWvT7y%nibjpWg(1Kq!|@hi=@G!p<-Vmj>Px~V9m|m`j$IP+AfRiM z$QLBBDFfr1ft|ul@Z=f0YI}A*g1%3}_0Hn4B^K9%=6;YU082M zMfm!zAEjj~l?-~NUFhmj9QL|&92Mt*iW^t`9AEX*K<oXF&{EI0zNJds<0%E_yeyaoZA`ohLVV1-G30B71YBG#gFimD=fyGrYf zZ`$I2;FqW*g*D>!o@KlippcK$42IL@G93J+9=OJy>@c^8^sJ1-^+HSYt`;_XyOYos z5!~g{FFZu zY6vqf3C%AFAt(ONTa{m2|K#iMu3P=&=6|_JLO7#Id1+xM?%Y`3DV|x~GZ!3l{`t&R zdh^^PzT)KHos(I4cvY*zBL}C?EpFeTkU>Fl`@iK?c}8Vk*DQ`3PQ1N+=h8Uh|H?|b zb;*u1_VkkC4qa~LBCo8t(oyC{v-XwqP1=_hdEYNXtkbQub3x_!^s~##+jeGrTUmKo z6t{9dTuTuQ&ac0vm4rMe2+yyxi^|%UIjGX3M~{=RRk4j)9Tk#iLNc^#NJ@HFVJSt=RVDl@-&97tK-HbC|?hn;jz7D$< z%buLZP#@;OOYjQJhqqxNyaBJnBA5$HU@4$>&a3byya=zs9C#LH!^;`F4C{T@x>sNN z(Y_C!p>NM~PJfQy1?M-x2e1a5bNxH;R)&>W;~}S3a{XfDhpl@cw4dr&gbi zzXb1poVj+q6tgZX! z!AtNSaGtTovln3P3(xbOI(!dmtMdt1HNT_9@ZF)*IMaI{n0IHjNOYp1xvk>pJO+`5O@sKst+s%&nNbz4t77b9`+zu zd(R?=q$*Yoeu;e!27~_8$og6jwKdO)Fb%BR3NUti&-K>9J+FatbHVfJjpTAHy2KpJ zeIIidV(rlfVI+)!S>QP@XK;T-2ID4PpO2`?huH66A-?Kj_3PR8VJob$*$bji{gY>W zrmUxat^fN#4<=2q?(se3^Q!LVGzGSTwWlV@74T1tdqO`L0S|-E;%G1~^;HA)^xCu3 zW+iwgy_M9$qC=c>p1fjBUH25Y*SPihBB+JB&jQyT3+8F8#-|V0neQ%8kJX@`-K=en z-46EA5Eu*Q_YAxNTVM%LS7S?>FxKx<_tll?2YnTtqpZG*Z4i) z5^QU(%isY}o1riY>_7dfkJ@sbG{UO4&yAX|2Xpy?driSS%wZ?vbKxw;_ki)*Bfihn z)MrP1tmy{U1-P8C{r@-^uRUaMp}djP0@KKwrOylYujQHzh}7`@y5I0Q!SEKz#oYPmFCo)_&OtYCIk|Gv+*n@tdHA z#FX3sEuaAG6Sck2Gk^>2FVb{`W^{`xjqs5EqHbxxF2{2*q>3e3a*JK z_U$^x%fS9nL(kp-#?CVspUrKI_kd?OKMEcKWAdKysGoW8o`}8YGpE-2MXT6HYI6uY z(`QY8^m?McJ;C+n;Pc%V-UfR>)W@^ez$W(e#y$!9R&V#I`BLsZh;{$d;9hc#eX2js zO&*4F#`nOT;JIot9MpdrWMi~Pw}L%_?-<9|*p)yW@!jaN>OQR+?giI7XHCg5*313A z*H*(PV64@#AHsCl2=&Q9GibOZuK;e#bE8N!HZz5=EHo_ zAahM#Nk7K+^i)tM^)n7*K<^|UYfUAiyS853b&o-3 z#_H{Rz-wz@e!g#v!~QVdg%LiFp8E9KdU&2XT0i?=-_}q6YPcJJ12c23dl|lhXF)$ZVKrDcYiJy+fL>Rtq!V1n z^<~fv)O{YPnW&Rz8qf2fuY;NEmDtVfe+28g5n!C^?^^dTpH#uBgZZ#N>hvy_ny7tz zU%S@&l2_6fjHwI;z^Kak=ue%^-L=-yzSFPg+RJL>ocZEE&acOA0b)o3mi%LXdA6~> z1?r+dEj5boQtRW`{?zA3VD4Xn@r%!qb+ESEKws+b^JffWKrQt366aop^;vilw!$pf z1s{OtsKb76pYiVo{nM+-Q`lkP9&2N4#$~O?!pzJyK9XvTw}bWijO&ZBTYwnioNK=a zbINL`zkRH&iAB?-8I~F+*1?$7-5kwj0^A8bxql*dGzV& z3K+A!U=7U8T0RYvz!RM`^{0eJ~_QD=8CQ+yL zV2|&E&nijAW6j50)rL6Z`}-lr?y;`!Q9o<%_abv~Y}_w^`LH%=h4oy|nFp?OzhmRH zueO4<>W%e0dN{csYj0Q!{KW6a_Jy&z#$c2E4Q>@#%@JPK+sFk@YB-NwQ+ zP-k_dR(>8zu7|o@7s4H24_Iq!Gzg5_o-;?|b&go$^KJjU1N!q_!~K|FCbzhcn0<%F z942AaT&;}9{rX-G>beRR13jG##(K{^K3CSsoYl~C-T>=q&GltHHnVRO*8I)YxSnOM zBeoaVL&oV@vq2rzwF0c2_0^~2?d)Suav2nHeK+)m&M*LG!Y(il^RY(DK)vm0YwWY- zGm77&1Zy9-&phUX=h>rb`)8n@F=z6KnwXQm_3;WU1Y@=5J!cINXMFBfU^z2><~RP~ z;CbX9^)pV-RXa7*w{z;MFV`Bk{i)yQU=MtX&jRdMKwZ=}{yanN)P4lmm!3ZfSRdbG z=C&5}X*?NUG3V_0)`@2|#{VVO+M6peB+tMDj2{MbxgUDKNSF)ONllEyGu1n*n>AJ| zu4C^_X6!!WK+hy#&DXOzC;EFH`y;qd4fMYaj7Od9$-dafU?SK<>a+@WgE4%}n*Lbt zj|F{uwtZnQ6}DW?PUgr{IW^ZLGm@h$K~ng6NPLT@M)M0E4!?1l|He-DZgb{$>_>LHZMOUATM4#quP4vh9xaKu1 z&rb3r*4W%X0es(BU*oi1K1*vd)-gR1am`|&p2;0pV?cu#H#Ir@y?+7Y{h&X`%fY?$ zSo~S1bNbx^Zvpzp=g&Ub3!ZHp8(3c*>zPY3SQq_ypS{UQtUan`Q^9rF=T-maZfu_2 z6uT2!j8&t18UH8NT+lUYsgCHAoR3vMpM|;Lyyx04AA!Ejop_R=ShXAn`WXxEA(mM4 zM;M#4@0U%WHtKJGslU&~4A$mjxgS5H4PZPGjDH8b!MtbO2M>X{a#nnv)xhfNgueMaS7Z7*3_OgKJ~OWN5lJIY!&bk zd#HK5HfQ~jL+m$o^f~iA;=Fnow{fh417N=TGgf0XUwv-@{3mMq0-#y)N1!+3&)@7t zbJVZTk8xT%`@-{Hg(qMNpjlk^Hg+Rx-8UA9BmQozm@z#R-?#RjaZUr{q7UQw=06UM z_iI=LYuRt^>Su3QXXlOIv&33b_n5QR8omb3e+sPe=Xc3(8N1i>T&LFRtESdjpZfm{ zT(=%xfe*o+=nXeB9sp{fA7fP?*RO%yK)vESN`3TijQZTf{=V43V2o;~4r=f&SR2_4 z`g#YxgAdqezxymxtN5O@Htx69_M2L&xxHZC#-(=ufOXAwcn-)r_O9_y26Y0zcgFn# zv958S{#Jr(v_HZZ;F=L&KP?5dQ>zKEn!Vn)@70%_|bN`Omo=-+ki6@`_(>}1jeVn#1(&*VDH$o?o*?0!Mb|}YvX%SKgNg#v4-lUrsiUQ zsFOMG%GlYNIr{=#;`c3m8>4f^YuxV9s-3;7AMg8o+i%1X-}|!|8?)zq&0KZtRLJ)B zCyaN4`=$VK#DBwi-GbAF7Ey?bW*2$12D1S;=a$mbpK3-+Fl-XT)CDhv!ZM-!aCg zPx>K#b~YZgjsMnB+iZ^R<2t?{doq3+)O$R0^&pqAXPajo>{swHc)q>m9?vB1n9p*? z?swk!tbzH?1M>6zn%oW7as4dZ3ccY$F#fUJGlo9Ui!t;5TRo>O;M2cFIQ;!t{nVR1 zF&5+TeX@jgcVP#E`V?dJKN;wgn3Hi?A2nYH^I1$_F8d5@<6bka z&2>Ha%(#x{EB-8L9M-&sfN{Fcv+Os0SRc>u+FI-ceb&ILr)%`L9jH%yrhK08AM4l; z>+@~wdw}{TW3Z3F0I-jYp#j$Sn0e82v9Gtia?2pe2@x-4U>1QeE-+KBkGX`^9 z2lPUW!}!&Tykgw1Gd7;Z8C>g}aeJ2eXYaYrGv;P64)TxJ=C~JJ`vG{~O!yKwFV@7E zteHNxGM9s02Ii=q&g*3k+;1H;@`I3k4<5-U`xOV+OSPj#_JzC?kzQ+DFm<#XIp3jwzF>l_6P8}8&)|N>uIg@W32mGcPe%ijDSA@&tm+&b^pq-dc|kdo=`KM z+4#=3SL^}5$8cuS3+vvau{*&y)nz1DbM@C!_o%tL8OJL2{1$6Ztp@ane;1(tIiN-p zVL7PFvtSO6^>0t=%ev~@bzg&fmw|nQ9@+On+-Kd@iMqyK%lfm=tm|4(bNfsVfxh(P zS-vOSy9HeLJ-Elbmx6g&6R)ikI$PuT_a7S=n~&?=uOBtE_Ufxn#<2tJ8R{DUE}$W{ zBiDC;J?MI4(T^HfLu>O95O4fF(fy43G46wPpEVi^6F?oO!ONhI>YUZs7`*TEU>zCz zJNdZIeC%&EGf#WQSiS)@ng{A@PCi@apl+;*KST6vYwcN{Vb1S^aj3_8fL2j=wQ;XG z>5o0}edD>FLqd{8^{Gd|BWM%URd^qxPPNLn!-4-aMRBIbHB?g7*#>R_LE_AnR$>bd}k zIrgx%wGQl!^IPy?%)>#g)J`8W;0Mr`y`whjtiJkFFMUk{)W-F^D@+4Fp2idt8&|KZH_My&bhBdeFshjG@#K9BuJtU0J1dHYWHJ$v|X zdDPLGeF5Kc?=(1yF?-|pb91z}j6+s{Kz?%0si{Ed+FA2;!lvxpWsj^q3AitmLy!wGp0 zzuvepB>gKwGJ8cx)^!QVp4owW3$3x_48kc2t{C?V>)RTH&-%t@mF-wHdz;)joJER6B58$Za|? zQ$D8>ZcwPs$ae+RPTOus6MhVR9{#Ys_%X^)TnoL zsB!LTp~ek;LyaCQLXF91gc|QI4>k4=4mFQEE!4dDK&V-CZKyfmyioJ`Cqm8jmxh{u zxi8deHYL=$v?$c-`evv#{HaiD-W8$N_Hm(ht)fu-^iiSqH5Y{1_dFhIk8c`kS1b;- z_gx<9G|CNiF02;nw7o9WdF0ekXXaI*&MG*tH`HyqHq>qNT&UZrU#L6ePoeIdU7_x# z-JxEUg`wW5^+UZY>xO#W$Ao&LaznjE)k3}9%R>G7HA4M!YlixNI4RWcxii$C!u5*$ zP=Ei;A@8`&A@Ab5LtfG6A+P@rA@BJeA#Z()kauW&Xwa-_XmIK1(4gzc&|vt%&|u!p zp~3d9p<&GzLc`OCgofArCN#XKPiQzkKQw&n{Lt|8Jt4o*@ge`B9U=dZFNXZy4}|;~ z_l5jbV?+L*PYjKIc715nrhjPE@%Yea@V3xsPFs3qdbjrFh1dzjMcVlFlfTjD@A^v% zyLRoIxu;3Gcj#PJ&ftWpT?&h_N3}1En{&MTyDsIWcHfCpTVHiG_ShqDUQtxYpX*;5 zcU?BMurncqHb*Wv^MVV`ZE?ogEzUZt;8&#`%GwroDQaHOx?36l2}9=&g#|4N+H@%| zDlISU(51MjOL@Vs`M=U4oHU-=uGoO=}) z`k}O_Yv;ndZbO$_kg8p$TO2a~lhU*}qvd($GkN|0$#d7D_+OL!M7c9so}+R%{VyD! z{6B`wfB8{dcqeX-K2oP&p4I%!^Ueuj(3o3Gi(;;EybDxBSgsjyXHLw4EoGvOWrBd=*zHXs{g&FtMQ-f{*V8{$^UDe=*s_IRq($@ z9;{kZr%KI=9AvFi<^O$OCDw-tr|>HNc08dUXt|1-ur8#kz(HR*U&zJ`q|)#kW*!$u85c9YI0l%fho=YRiuUx^9n zJVD<5FL%|3RT_E_Z`ZC}HpUc;_Z7^Lkcq>ZSrg)4CZyy{;yvkZV~CO1giH+^H43+I z%(Tze=SmI}o@DvT9`K*nxnzca?I0M9!v#9qvV*LZFo`oZ;`w^%puAI_5; zi?5x-*L!doyKtKMo4@DYagtKarwT-Tfn_>o+;-IIk?4`9FwDYaQq#v za}?&u`k6D=_1O5#?KS3m0k?7NK6o8sbT8g#JRT?WUSl&}V|R|^97os2+j7o0uEU&J zPh+yq&f|ODlN?8K&w0-Mx_|eKcnZ9SjJ$ST=kY$*-464z4aTwpUSnVO#u$7HpUbiM z2CXof`QnTtla3Q-F=`n4VE{T~6uegx;B)VM-sk9R=c>YJJl;PPgWx^97seip1L53X zqbtl&J9v$Ajld8LMt?ZpGWZ#0JkIHU+?Vf@|qjXUrT)!I%!2 zFc0@22OdCvm`~%Ik1ps2KL?#}4%}l)SSQad!AvZGIdV<%7QdJACA^NO@f3Vc-$z}v zfa@^&d?Z%H9pJMeMtx&fInIVkC<*iBdgj9QJHY&S&Rnm7ePj>K#dsL&VwiVx)*AI- z-p%=DIG^z^!g6A{jnVntceB(9iG3KIpZ&(kQ}}$%$(RL&z~15o@CqJ624uwJ(U_i* ze8*$VaS?yxCa)iZ`}W-LID$*qg1A(V>E8}xEdS5XH@)a#&UQQpP&@V z!hMW^`7sweFcF>Md1{r|9(7>vG=_6|&VBcU^*7(m;B`$<8`iNoYT_$=iIVWVIka|d zV2y||pW!vG z^Ez@O4|2o!=$nww-i&==KMsw?!O=N$;~x5BZFKw@qw{@?|KKCMiwYef*S-$p^S$|a9#2N{GP>qs9K=pI_XlVS*X)ClSc+AV zC5+DXee~LWd2R;AqZjNm$9kv)pW`8ziFp`{jWEyF+`PG;DVT`UXbodCt{c?&PDY-! z@J!yr@$c|D&&8tinR)#$F5ow8!Y{m+hVdxQ<07w_D^3lpC!xCDE1KiEg;t6hw~zZ#pPaSwB(0H3At zyO7BFKXDT$;GCnd1McG%*WAwNyxZZN%n{~2&PZOudn*`!z-U~F&M}7hjnA0$BmTf{ z7~2L|iyl}6>$V>5&)2SLuFdONe2-HwX6KP{{EUMzzLi*p@8CSf=31<6jpEO7JQB`7 z2iDiUyBG6dP2D@AeGtBHFh}@|cJF_~d>eynUgW*BjDIoSiTjZncfpz_;6B_PUE`dt z`74~mwcV3BcaQXvIfy?4=Xn;{IrlpwwFrIYTC5pjaxQD^e$B%cIM+ImFMBHf6qqCQ zV{XQG@h~1m0c1f&5P#@_1<@QoF&d{ewKsNxdW1e*%<)oK_uViz$Kkca>V088bLIZ5 z!x|ib^ZNXpKu9Ei}zAIQ4=Qlsw;2vkfpB?)!7DF)r`!NG+u@_!X z?L&+s80|UZaXr_ie&N`d*q_fw*sJrnmrZb;YjA(gW$fnNzT6J?McqQb8>4kip5M{? z&cJ(F$KD9J9LP8t*2~_d2Sa_03*r01y^Pc_@oI$r3w`wvqxCYs#1cNUGBH}~C-6F6 zf;D~xuc0vPt5=Z=UiTpPTigGUhj>R`b3R7gG4dWGU$Mg=h8TN~(F1-LON=ANYx*&K zC%c2!#*_i~fn3In&vOqTBkccFcmzBfiD{$penxsS#{ObAqVvQW?&Ex7b`W#;8R z^CJ&RM#?b`K@O1P#5Yh7ToeC*aUDLvM;L%H_!h%38^f^@i!dFVu^bbz3==RK^ohUc zxWABpavEw7=Uf8#TuU^6nUjuVml-)9yT)jpj5!P3zwuE2@Z4nO8f$w6$MFN_>6eg0 z`X{k6%*~5<4CX@a&G$N^4mx5R_x}m|$l6;YV>0g*K)pi#%uxxh+XH(eJ<`GbeFf($ zj!M|gH9j}aN3YtG;o5f?y`~JlMiErUm$0TDSA+AZG(JRo7?bgT3g@wwRnZQm&o@*JKJ>>In6z-j#jh*A%LwFNkpcXo#A(Geid^4@lQPqE(_jq7<7#7ohbkFf$eATQpBF}ZJ# z%}YzTu6byUrZAqnVNc`%^$b1UjnTRK!0XGP9_wFcOb^$Udzg;7sL%TcaEs%NFwfSi z6}rRTnt-0s>j8|$;dA7B{i5T(jNaopd%X~>oBK0g*5EUktJ(0pd-lA0nuLX@2_-JBsw0&I1{}w1tVcC+G8A+!hL!?2VG!a82>u##}Qn?Rs4&;u^St3 z4O?IxCc}O*2Odu&K6@l5`8&njnR3}Z>osd0O~d-dAU zaDOYY39hpf-(fHI;0H{E*Q~;FIIr&?Bu4whXR{NQzfjSJL`U>RIdnk@G=O;=i(S}=t<2{h&0Tu%yoBes8{;rc zg)vx%XjlIuU`Ydj}#5ccO5m@n)1Gq%I| z%wJvjte6Y0H)hv0AH!gb=E$6nz<6wcv3PzATEkd)-_Oa|Zye`>wQdIc!u%PZ^T}9} zV;;`2{pent)4np7uH)W(7LUT(?#EU94)@|dt@Amafz*uG;5~=n{qDt@nENRpZ!zWw z-vy~t>=G`3IsFcpn3C~t{K@e>NWt+R(Q(G4*WtUN*Ku$7`Ee)X=O`V?!}vU2L}uhh z7G%eBxEl$08ZX0hIdL!AGA}*r&*Ct5K1S!Riw0%)Fn0k5+z8(>b%{Y9LK zczi0dp7A8iDYc8UUc5cW<}4SUz{hwIWl$WgVEj$d816B7ulAKOZpS_lx4#?2>0b3L9RpXjMei^&;Whl-bZ0Lnqf9>f%|+0!td|*FtY#f z`}JksI|tX{^DUl=aS`0l77WB>48qE!`~96GeCIOX%di1I!Fk;0Z#aTkSd6W3zs|QG z&Tns-BYWfk%&)mJ_a|`$#`Fu$!Wygtxe1>==5z-*ANJvT_761)H8Y0aIcA?>w0Exa zIt3#==ii#b-x%(V&Zpyb0>|EG9Nx>daci&}#%&)Nx34|sISRdRtj6Xw#_DVC5Bp%Q zkW=;&=1a?Hp6t0-V7f9@qI`M<98o-!s~A1wQK&!v3v0R zvtZw$*6!2$vLuZmKYYN%=>hvVS;1ZH9zreOmnVHSLU73RWw7Q*+}(}O!0H=zvd zFXywL7GWs5z`ls#VbsQI48SOOeP7t$twCPG?{k9~JD?-lz#0uhd$0$8j)lK5w`A;x zM(Bo~Xb$f&7f<3{)JJ!G4P$bxTBru+_z)GN;|7ck@fCW&d8@#C%ffp;gV(v1qpHID z>YzN_Yh64~ycZbn0{s!bXPGa1JQeRPhR>PrwSch|M>DvuvAll<6R?@rc~A*?krM7P z2k+Sz=^3Yje8uSJ*!P^f4VgKnhyB?fqd#KQEUdeq_tK&uvcp_Ceu8=+XR*2X2`6~X z{FEr^yKoIg>tkNb-FWne{bAp5Pw2VRjKm+FQ;x&WWuBYxvm_Vev-kq$&%BHSdk8h( z$LMozZx+G|7>E7%7nU-gdvdSFNNT&haSTLov9|vS@))C<*Ie&8?Gl&xw(kh$i?I*2bPS-fYN%S744mf;}YTnuB(r zKH)pU7{)Cyr<#l}!JJ{;HyJCV1FD01g=e`N<7{|OFZisPLuwg54~)h4lY53WV#jbE zH#naa=FUF0E+j0;}2L*{$69WK8>Tj8e_S_tc`ge3VYCFbLo1EVcg~}IhW6HY#kik zqxr7|^JuO%V?Kt1y@lUzoYV8pv5mOg-|Og&$BB_Vgzu`md7T>fqYA9Cz0ecJHVMmN z%=X?e>;(NAJ~z)XK9A?{4wyT9_cG3UaI_EGz}gp!#tDqZ?|v`Ab*wji7P|?1{SKsq z`+f;NZ$2B;EbMC*qj9mn@N>ZToOcGMqc@s?eZ+pp4UQAxvjbnpE^?e6ui|C+thB_t zr~vnC?J8jkdSNv89S8XcpAW`458H7D|KcBuWDRp zPdUd)T;ROdCq!c=#@mq+#2CIGxW+n;jh#5czty-uo)v$Nhrjt|W^_;Hy#_jC8O)D0 zJq+`F3*;w!mq^7({#`$OZ(y!?Hn4}#r^6ZNzJ;MT|Ag-YXQS7TFg^~R zg|LSdjPG+zQLv{_-#Zzt@t3e~Ctxwxac%fa`i+qo{oWRGkcH7X%}I9T;{8Lgx9X!F zx}y)4U}`k(WwdXtk-6R$%+jmc)BY# zhw)}$+yQbFYGU46;~V%nvJ|^O{=)zE$6RiMqjm9K`_o=xPQQbOeg47magJS!oP_5$ zKVxZh#dvJSci4yvTz^l}xsbQayf%M+2HG#Fz`5|en7?6|k87ZR!*@XQ5%qQGKYNY3 z`28__{?kk0d$n=dSKd38=fmq>1^piW&Rm?)UY~&3AhxiE>s)4zIMQuQ5)X!& zh+XCQF2wOVtjCvV1@~)R8lw^3hwph!A-JB`48&YcUK{;NB+TJ6MkuaBuWj_!&oj!!y2X4WcFQY1&qc!~8nE=-q3;S;^F5)15Vy^objqzPPfY(qEMNtZ$p$b~Uz1Ku# zSQFPVU-rkh7!TqPJ#jON%UWbaP85KB(G=ET9cJJ#-0K3Y#8g;|pV14QPzTP@4i%!; z?r$2pVgN>9>f)T`W?oa?AzRvgVQfyKNH4bAYAto6oK`ukM8g~_v(6H=UfXh1-oHg z-2YPiLjK5E_&hkqxCf4xc`r38!5ECw_!eUcd}htNwV8+6pr6Cv9=xv@a~x*88xO*t zKQG{YRD<(YKn<89$FE^6%3=}hmmaXL^jM7fjWf*m3}b$ngOV^0_OADJLq9BlJ>3)L zuRYpg6-F}G4IIa=XuQCAb79;maT|>NA-LYVC=To25XSAkx1%E-=iYrdft$Qe0ry-E z#?}SKw;bkjE5=60_M&zE6<4qpZQ%OlBjh0L%e-5UK5%{KHK$FZ<1vi(k-40Y74RDC z^EA@ok?6P}<2$GWd%YQ4$K&cS_5v`cpGK^SuMZ&&3c>o-Lj#P!Kn#F+_nDuFN$@#z z-qx^JhhPi5Clh%)iBlY#J9{QAa-%4WZ9IBo7~G>dupX}GXnn1@ag2s_WZqD>oQ&q! zobACuSi5of1uLT0)?h!5@N6W&ILKYR2;*aT1hrsa8HcrZuJ!l}8?YCDU^m>~E$$~* zet!+$qqcE;2gl~nxfa5GTf1fOo}Y0RzkxXY-zbIGu465UIsOFh7D)_-|Z;dmo9Z zxPa4G0&)=c%{_@T3MPH_zs~Xds0r&j1NN);TUTq~oW^G@jLrR;KXdJVpG0wvz291Q zgMBas^Kk-aV2%IfTH+1gqcbNRhupgtFG`G|r|xH@FT?MD;~6Kwx~_t?HAdzNd!g6D zJ|E(Bc4Pq+2*=qNbHbi+PU}k1!@uKkZDK)uj&Ya?dxqSFzwKn?9` zJ=YRX=w0>`{)SM9*Z*>yiP5~0Yik`o+lW8p&c6B%^I)HCgE9UEVj+a2p2@?=T;a39 zIUj?0U@dbVcYm9yvDc8((6?0?jqeOja1C_`zyA_L$d~cn;P`DupH*UvKMVHh{zIQF z;n-MDGP>q%NSlz(&(Bc5ry1$z_&f04x9}QrfX}?}dpC2%t*7}ipG8qQdfkL^9Pd-Z z@cDI+(f$95^PD$d_7l%ncuvf1R?b-ubLyUAa9(p%3T@CGgWz*weP&?>Mq?d3e*j0Z z7tH4;MT~Rd`_V;4)(?LZew?ucK0zEVaxdP7V8eY>I{~$YaQM2&tQX9U?Kj5&pUMp?{8y#3hvMKTA?E*U>~-F+J(Kix2>Rd;j@-F z!q3sYjPBf&AD}*Z zp)sn!+)P0udeEG|j(cI;&S6hjhZd-fuh0zDQ47vr4_>nxUEp>0mbqF)Y>v*E8^-J$ z_ME-k6R!U?CSobR#Y$NB<(LiY(g)VsJoJa>M}XKvzxsY_bcB0ujzMtFIk1m6!}|_l z4|`5Ue4b+rxi7|88qVRo&NmnC-+r10<78hUrge-bna6!R9LdaB6ZV+zAHxs0gtPG6 zFNmW6tgXFK7JcE~%&oa1FR?R>>0#fzh;lH-_81A{H)q{27}nPqeO}D7HQtTk=#G*o z0k8F%nb->B_5C$y0@r^YpI{)IYXIiM+PF5)oImqpKXZH+UPf`0LPIo$&tg}&)~j%j zJunK^WfOCxV2-OC$50Sy@BzGUI~L;#HewRo?_s3FBe)N*z%{F32) zzOAo$Xn>BW0Pksr<#3-)*sr+GF?EdHVssCGF=jzpJd6kN915c; z@}VW{{eBq4e0MW)ZTS7pJ-VMa@fqx4_p}gP8=mdzjONrlS~p?}f3sN4xEqJ@I67f2 z#={(}#B_MCb(w~Ctb2g*8PIDXU-tSiIC@R;yiaia3O<5;V84BXfpD$+K4V$|=O>RT$dlhGLLHvsTum59 zn@A5v`?nA3!@8QAN!WxRqSvlzK3~8+C|<9`V6;Wc~$bLo24XChpu2{|N3{yg@xFwFBYqd6Q3b7+mc-kciqFjxy?^4Ylz z>(Uw4Wg%SO{qMs9__^df-QYDtSl8U-g*j^u*BFDnm%d<9V-RnQCLup7?jwO`@_ z`8$U5xWenyD1=Ip%8broj6U0AVg43lDz?L1Zo_x5AFO*9=G%dvIDP}h>VC}kA##(0 zu^;-w{JMs5Z-g~+J#+OlQh}b1-Ao!o&vxM0{g_ABbgu63nOO&)pBY#RY8F0km$9y1 z;&ag66J9} z+=IPjE{xqCF;?d~fIC?C9OI+NkJn%yR)(=#v&GDvmXX+Ew{VJMum2FPW1Y=iTXaQD zP?zxCh`2(0u4^708-aO3ov$rDbukZ1z}(^YWA`~9p6|l`{$;EObF~_q;QK2u1!rKMmSHUMB z=X~TLe4p#ZI1p>G4!dy_-(flE8~^?m{%&;_BfS)I`Vz;l;2^BYNKC^?P~+sa!{1!& zr9H47yCBM@Ed&JV{#Gtt2(1`ZGbVEQ`QROFO1YU{QJpu=6V?J=K_4~=MrYKnS(nh8HyE>WZUwCSYsiTT_zH6{1NPton8#o7JKAGE z%rAQj|1M@t;&2}KksTS)2wry#?(s@=yp@r;&$#@I_FWP5 z#W?K5QCtE&5qdZkBYX5SH$1!RIVO+cbK?->8+ZmWROCJLlN7uTGRz05=3gzw*8J&!!`sONrZ}_wQzW4lnS&bufQj z;dSX4sbBbvw%4ptRt&;a_zW2*yw)BJd+=JGr|{k>Mqj&6pVwbt{m4c5{O~&0^}Xw` zX7-3Z{0@9?GwiPomH8gq5)c?II2+Bp|I|+!W@2$kr)o2!^LnN<8Xcri(W5c?1gX89PY6T zMxiIBV@)(#8{hYyX|O(y_KflLz?bm3vghicUi5kiqw}^xKa_@RI<`eSbbx*BIq&-n zxljs~Pz>*(IR1k&@EY?$9>VuGk87eKyzfK2gt{mZy`I8Y5T3W**^wF4HfE0K(Qw{a zzJNLH2xD|!^U?|SVK z^I|@Xr6ruBKAg{3#-Tr4%bYs5>&<|Bbsgt0{{zqlvoHF9V*3>wDjeutxddds%S@^26TnJ?DHG z_IPW!r+zRGUq#%P@lS;3z0PX}!abQgYwPh?m|J7E4)xF!?NArVb2jIA7M#oc__SGvCBAI*JI~!Zs)PLjo;CGMqv>e!x|2Owf7$P z<~`=f_l(g!wny?>_Nx2zy@s#`uIqVg>>Boq`7sCT0>42JpY6ET0rX0Bb=7%Rd28LQ`f&o!Oj{rTEBtee-lw#S_? z0zMbZv5L>Sz1W7|d7T}}xiSatYdf6dCAeRoiIH%B?s-1U!K~=@L`Hkap7Y!s%)kP$ zUiiH9J?l3W#@q%=Fb=(8zFgD%c+Omymt|NB-*+#~um{FyEXM9y!{FTRbpU*?Kdi+f zn42ju_l_%IPV66Z<@~<3PmRHz>*z@LSJ|@8$xvxPmrkU_uJ0#a3IbQQ)-hD=m z*U>!EJAQWyKU>|S>luT2G=J94Ifg*SlU%)&Tz`J-KpTg_j!e@97pQ9#x-#o8GAv8o@ z6h{sCnfo7Q$`Cx&=qT!h!B2l2%}MpH0%oX>$c@x@Ex6+8-Z5+^@l-qMU&@E8ii_j3`0 zd!B=TdHoy;M95>jAjplsvxVcjjO5ned&28dj8)(o#_atqnaeou0yzl(+nWa&pTskG z7jAj`XW=#OFB8a1`1{stjE&F}lW_^xa3XS@@ei(dzHD$F_e)QNF@Z4+ z*KEKp+~Bq6(jo<~Z!u;Bar<4|wZmsfZANk*p6eNmM|KYk8j3Y4x?O<&4;X3p| zaFB6324Ele7~k#Dn3K_bJdrep-wB-W46pA8@rA#8T;}+`$m5JwjQf z*_inZ`Pt0zJscN>wfP!;{<)Wh;QjEw)8ZcgJ;Ypu-`BE7cfxhdr}HervIx&n_`TM11Bvr2BYO?sWeYQQLwmR$ z?}yLEag3wk9J676n&%(!1HMms9pA$EEhfPE_rr5r;k_GSe7i6m?sp7~cO3>}6lTC2 z{fdt<7Vg9MTz>_;#<{6i_!)GB@c>-o7*66aJm=h(;kw3t3ZDOideTo}otqq|;`lH8 z4evKr_h@ez%Xe@u=G(Z7!8)cz8GM4OXb0n44EMbpgCgEP4~K9GzP7H%;abLJACtTA zccmD|=iy%VUf(XTtnVL~_37a{LRNcQqEk9Ikvjdpr!z=REG$d!5f3x#lJ;jb8hnpBZWKEVAJ(xc5&`7Omi3E22H7 z!u*?K=jj7ubAQx31-bHjN8*FXix1G2|35@tZ@gY_EXLFq6X1EDTkGOH+i(V6XU@E4 z4E80}$uT@ve=z>cxl~|Zv1`081p9j@*1;TF2lG6K>vA!=SNHcWs-X#ts~#GnA3Rq9 z&EXvGTYF*O&j9(0cZU618up4k(FFtX1N_{f=Hc(!&Sg&?!f6nDc>XUkUgDaR$i#7a zq(**NkBYGW?FnNu@5bcu8k~T0vH$S5{uGQE@DQwnao2}&{{nM*0heK}*TSATh(+)k z`}8c_ub&IX>|R{U@d@}`Q1j4Z^BKK%3Y^#LCcw`Nk9{uxfbU(!Z_#**7@z0Z+;72k z{0*)Ne;0LM#xfn&!}W&3T$qRN;QZ!u2be#_ok-2`U3eSC@fFOy_d1vJti~)@D`Pzj zKfBlC94_GiwxSaLf_dQC4u2az$?+=eg8TBbvl+fXBMgB3tWaj_v|yX9M;V}3_&quLjf4aY`B)!x>w?izs_hbcEb54V;QWu zd*2P}=y&nhQI7Ay-KZXMPxk(t$azNN{t@=^S&#?6V}}2&dyM19@FlN};a83mkp}l7 z6<8yDr{iAN4Si}X*KiQ-*<<1g`R6#EhvPaJgSoI~_KNj62Kt*2lIpsa@gH8_4)zkh z8xlkK%(378oA{lIA1|jMQ@lp z`X}s>7{kwijf@)-_J{A`o^!6h;a=RUeX5K}jn2#+8KUdG$e{JA(Tin4eUAEO2RO+ z2yw-WFkVJ?EXFa|pWe3w`*0rS%=pc_%%^)_hy7Ry?=?@yVGiN%F`<@c80*4k(msmA zn!DCqSS#mQh^xFuZo`^f6OPaF+BpB=bqYKlU3VAbR=BP;G1eXMbIzW3PuE}#T+6t) z#{LffE}fe3H%uYdzLyc!Zn5T9}U>Fwd^L4aOAZ zFZ>%Cy%v9mb8n#{TEgdNCT78TreZm+;6B(JjIn={g#R50wTbyW#b6(%Lt13VtH_Oq za4+1i*WCl(w>}wQ4|f58NSEoPr$yz->P0?^!|77B=Ug#hPgfO^{(mK z&O!de-!bUr5L;Hp+;G2{kOA)D0XSb4@C=2|5T1kZ@9w;3F6}`-Z?cw#Ju`T(1ipuf}y;#7^vopIbX{6rQs;H^JWa^Tqks zU>5dbGZw>pr@)^21@^#sOvM-&!yY)#A!25)A*NA`>>+%|xr_7eHw~|eHT;dm9!bw@ z?hWzAa7^C;@!rw3-j3JH`(d)k$ z%|G{rn%u{D2j{lnY{XnJUyNMEcuo_kd-xq{4r70qqlK6WYvDD%woXei8OgO<#j$lT z-!rflzGv<>!u?u1auQ-N_vU*7teyMlgmG|Q^JTpdV~!Z}hQBSn%DHdR55v#_6tm&n-nR&CF&_irUOXrFXicn_ z`91~L?akiKz&g`M;b-wHjO|ee)iDHpFc$W?_t{tWqP0z~r#W?gV`AU2n@Mwr9Mt4o zQ@Ahtr82%jUL+tTD#2@7;A>dB9xzv|6}!xs7Wq&c?y(l!XKS>>R1Ajku7NoHCu)GY z$Nt1Mj-Q8fd<^CY|DI(2zrsuCh$d(Z_sP9sw3p1K^P9gmu;;AnC=7#pJAgr04tvBL z+rwp$0Z-yRe25Nk{#y75#_O71KL*3m4Lzdc0gN*-7p~#jj@HR@lOxL*%dy|1j1S@| zRDkoFr=~CmxwvLG{^htRp2i!fgKFRz2_rGaE^wS4ccCoILw$6{XRyybC-blxZLtB? zcp}V+`5lF~VSn43_JP-W%_2DGuW(P+()aBdkEg&Kng0bK=GZC5$B+ia(FpIO6|5z> z2>ojhSA{t~SXaI9k zm^e>>=PKm;bsE3N^1lKV)_QFUw*Af`7`(A~yFkfphF&eip`rZgkhxdDK zE9_s7t;0X?ob&AmbqPN|cpaXp%$zF%bN3P6gYj7hpEF~0kLJr`W3)bgCYdMmTPE7? zd5+DKIcf~|TLOc5{}wKDoF2JR5U$q&-CD)XoD&k0{hB7WX{-0#zYiCEwsZ(SZ81N z!%XysIpni2{Elb-e0D!UcYFocwAWn6_>FH8Cc?bz!9Fa-aG0;1SPWx79KH6s>$m{p z+W_}z-~A1H?jX+aoy*wi_3&Jz;y4pt#9(v*dkpJaL-RKUywo!1ZGZPb9ZG=Ev}46NO!coXknHmqxB*jG)^36<~~^Rb>k^TO|=jTp_bv3Xn? z-eWJB_tVj6AAAq@bR;_dj&To`!CLLYcKm>oAUEOJ=9vtihX)wV=UMg^WBdi%!1LpG z=Qm<4Ou0``Tmtc5k35BF=G?K^X6TwXU7MVQYxp2lY|cWYp;*yrYi zeTUDriyXfU^IQUTVIJ*WbL0KwG5p;Am*Z#g7RtfAo5xS#x_x1d#=w4@iFKIfaV+VX zHRgS=7Z<@AbcK0$ZyVrV|B8<3ozQDu>zw9bFuLOv+>Weh3O{c~VG7(s8<<0{or4*e z06!zT!FBq>ycw(S)j=^hPc;~?&xfy*ho<&iZ&q=T@UYoq0b6$jdN{1)V9KLT)`~vs21=iX05bo9B$d_1&9$SO@#UwXM0=*jM&}h&TL=@E?xd&uVz>LAa;OIE!oWoUf0-_p0MF z$Yn_W;yD#4-FCj+r zYfR2zF3q8_`o8%#hs+h8onwsTI{bUU@#x%-jMmkfd);p^R`-@Xr`H&#wO@$7(d!9} z(=Y;?U=I#MGju>77_T)jpO;`<&gJ|S(F@LeN z*WQTnsD~NojV|zg_p%PwtUKJR_juh3Y=<$skNOw{*EOaVsEv7OjJ7C-FVGLQ@HM=* zD;mMM>_0yPoZI-1!rYoWlIKS%|gV$Iq*YVh%v9HcWqxp0{*3o&r#<-^A zB7EO@oAr>7>M04m-eN(IS==0jIQ?!=3p!);(Pe~oP}|C zz4N+uZ>)|O+co&?Sts-G2TsF1x>x6N+z;olM#gtD`kXRmV{w1hK+frPj(cFPoOfq* zY`)Bm@%#zb{R{5L`;6N;&F2kx-gSs6zLYVBMi`3ZwSB(L!xp$-*Grzy8k{fp<_1h4DV#MOhR_ zc02;-d=<`F6&2w;<&YmA;!PCA)A$5mq6})GIeR5nvE}%U*Y6>DZAa&72G{ujpW*?; z@Dy5c-zmnF$O_~88kIP|hS6(`y97Rl@$ue&f5&UWvH5L>x-dTDa=)H04cASDS0e5$ zFTTPXutwQX5HH|!xL5P%`|0r(%o%%%*-PH@B+}v@aD9wtBb z9W~Gz4dD9L!rGLA`F$P5;rcJ(A-HdIV-B9h!!ZBm$oE(){7kS1TfjN1U?e)DBfQ@0 z8sP}co&EGOtc5;+FZG4{d zm|hK^!BeAg0OJ%4g!vc+_nH;vL;jK+9)?{pAH_+C?n(KTA38;sd}n_GG( z{EgZE^u7$RzOH31E1)v0(U<6eJaE7Ev3WIz#@!Ho@e^{xeAIz+H%EK)L?>7m-`7|S zgL(1(@+b?haZO*FKXd*utn-I>1vzmy9z`~!Mn>2tzW*}ZgL|>xhU06P3+fyCwg|_* z_bOh4YrB8*@Gvq(W18qV&X@(6Q2^$Xy2ti2T6g1YiH@+o^-vT~!n`=|9q@kFb5G+j z5|eQSer7mFGZ^bMbjCMWfZ6Dcx-kFdxDXyEj?0Y3ogEeN4xWPhb3X4I1bd<@jM05~ z&s{L5?(YG-fTwXU{(~AA4tu_SWE7*Zs|;N4MZAgkVgKhqL73-asEPhCckbQ#wnBTf zfpgoVUSm&;fpL$(RJfLPv_I^ze)tN;;yH8uIjm)QOu+>7f#+TKEf|-5<9;87drsb~ zF%Cp#yoL%Whwi8X>?Bh~s938uMNxTGeV%{IYgVgS~=-Bxd zV>PbgAN+`mID+e#kM;N)+u^l`aSfNE?*=EL^QRbhVJUvVPTs$VaT(0na(J(=_hK!~ z+1D5xS;g1|&a(zbaR$fX`g^b&liPeh;de-LX$)iF{39_R{V@X5V2;|O7y4i#=CQ}Dj6OH^VoUr4WA{GmWUSVM zUWlh=+z(^tvqf4(+no^yZ3Y95Z@FMNbgP%0X&o6mZt|cz?9~Yv1@mAX&GicO!zvi3IWx93=n^sB*YO1!z!<#Gx(|bK z&WCY$kNGeL_c{*F%RR9xIE%};lqCK*@4bj;P##`u&+ULQoQjUEjkPmQavkwIRWn3b?k3rHOH>y-i^`steMw+3eUT~b9kNW_`dsfZSy$~)@KTg z**ti^*LqDKF8$;4@q!8cQ%5ll|bcH3+~6> z@O(2^XYX+juIC)ZQ4|GH1g={M@51-YO=Z|;uHk4NJXbS%ZGAoOTE^cI?ch0M`x3oj z-1cA>m}g_EhKeu`zSkVy>pm*LJsYd6zx(%jaCH8%Xbk6b{?V8Y_wSnKxIaFFb9;?B zkUi#Sl(lotp6CYG_kR1OE~dgg*}vXz|C)p3XU%nt-8__ndGTJ~H=ku-9&<#ad3^~5 zqI1^yJy^HA@cqp2nx~Nl_uwJiiLCVPzqo>1yw1dUGdjMT*NGh8V9bJe^!h62F5(P+ z!37-0KhZJwhu>u~GTy;C=8c_8y8pkQlb1PnjdSHt1KnXB&3_M=tB;T$CE+#h-F$o= zy>7+mGi49>8DQRgHmtYvl*gB_XDh=xe+B2V2fWTYTIV(}_tw|iS<7DVoNE}9?{$Xn zPeVtvhxaeTEO?(a9)>CS27};QuF(SC<9x}-6*-=RYgmEB@E-SMUIxILz6`m$xes&CGG!$sJLeZ)oF;eTgn zpDsrk*jo)yHF|9fe$E(!b@KgEC<^u$EWV{T(B0_*EN#^5#9^=F*snsng# zG-u&^*Gk48FdwV213R&eYvPOt;XYlbGq%I)oO2CU!FAm82v}cp<-Rv#A%0B~=YDIJ zSOI0D(d&%gb$;ai6pUx_6WYTbcF)e`TCU;#J+}eYwJ4mkBZk8|t$=H9gmdhMwYTQh zbSCD4dEBS}|JB2HQfd@x+={U;-1itbmva(h_&s7AV_Vou-(eGGVHbF&!uRa+jPA>M ztfhM}&x_$+%<~PdNnpH&PVidivHr&Gu|0N~>z+n#j*FlLsFC0G<7toyPvR}Sj;HV- z-oP_RkLSU4;d#o;@jD#fL}N5Vf2_r6T;hFd;oONw!8-r_HvxO&Zrq7`IlmXiXCC{3 zxx+Jln9)5R$8XX5&_H5bSBf%n?G=JiL;y9V_(_Mj9a z@yF@+cmw1^Mm&m?$beVD8u0@79IjIrJ{#tc-1}WSo*!j-ZB4Q<{t0tpA9lewT*M*R z|NHSbh&}YlKt_APIak6OpTPl`3-29(30R7QaF68M&$;l~U>>dgXZQwQXMV5ZAGo&h z=EQ>>bAPBk_lH_?P5chH_oqP3Q;^Ge1>WbL@Z42p90m8V7}nN!Cu10RCPJ(~G2X<# z-2ViMpdwmx?gHM#L|7l{=5^uPOk9&0#&?o22jdN#;`kw0-xuK?jPX<68;kDfjp;B~ z?%Do`ftrTjHJLB0w~W#FypDV%+N0ldT_SGhcqGbTDB5B!ct8AHJZQEZh~uH;hOBxy3=@VAG(*Ba6V!VpU>wQQ(!n= zeix3>dx#_K#b?W*D-o4 zb{MXACOW>wb&r7i!@ra8nH~F+W9Ihzd+Z>{Q|tg-%Xzsbkv<9k8{gE7u6-F-I8KFh zcpO>a`_AukmxF7Zi|4`X!+zb1d(DHFkP;c-99iK!4{^Qm{tEIG`;BwXo0DVXA#dT{ zvW$gL17D&x?4dF!j>_T9eGy$~@^P89FP!Jy= zH{Qob(eZtZ#*r0%KD~)g@M?5ijUF`5v*BFN<7HTb5Ai9?i}9?0J z7Q=l!foymi$@O+^=W_mj*pKXRzXf0&UO*n08)K=7;qaOlQ60wQn$s{7t3ZGFGd4UI z|8V>OjPY?er*&~{*BywrV2vxG0ZPF=S3yUZ@3nXiPvcD#h5g$MUNZ?T&;#E8F1*Hl z`x*0DWCr6OQH{-$@7o{l!~4zK80^C#m>2Ka4bN?a?@z{Re1|Pqh#bg?v~W)!!`$@8 zXjtP4F!zOEz58MV&ci-5r;Z)a2j-!8%|D<8GAFPG(%!hOC1o;ZTUuNRi z{0zf5Ob73|UdXT4pTxhYih+pEh3}Y;Gf(!Un)%%){0ug(mOq@ z_VQ=~^Xcn(SPlDc8rEVq#$z#tN5|&LbM^u8_zZ{d>38$`e!KwV^_g#lc327XW)0@T zd>GRJxVCjM_8Yi_TW~%<*SyF5*l*^-_b#&L?Tn*9%>EZXyPfw(Ooe;@o9p6?K5y=I z2dw!DtixGwP52&s52JP5fW!C!e{p{b#-A9|Gui{>Fzg{8V=b7!k(dQ@!?oeN?r)6N zVhyaZ*LdyMuolK~1v6o<*(bf=Ubo{KZo>Gsu>Ng~rz6JayuHy4`{3L=ldcQjaouwX ze1K-?0^^glIEbrQhV{`noAGz}Ob}Z2eG@ot0sF+` zzSxB&a82`h2FB0>_K0g7f!CaXdp`zh7q_AC7sjcFg1ND*i4R`ZW#Xo1D*&|J9C}Mn!dHd;E_m2ski<$mD>C$WR4Efhb5Z ziy)&Qqd=8VN&zxytMTcR7)7HdQKQBT&W|{@8tpi@V>DSA)b2PN?P!b|heSIXHNOA8 z5}*Cvm(J=>Z?)z6xesU9XP>?IIrrQmQRl&!n_*MQsMy`>M$q}-HY~wibrw5Td4kCSt_*!hBFXWq(vJaP2iZAAojf8Pg zo_(TE;(dcWAl&=P8PByT--hV--)vK)Y;{giWdAIoCv-`UTW@(0THqyx*eqg`)u zkequ~r^HHhieI`B*)O7R2a$6_U=ApD$eKE{EVfXeIro^k_%w?!SCzF}vHd2YPUW1D zv9cz{OF55LP_82fh{Rg_DETXMO1>A8KeX{WzklewghOdw@M;z!V732`v zL?kC<4{jz>FLN4+=&L4@?@P%l!uTk$o6552re5WH%?ZjQ zNwM(<_K`LD67ijk6`Q&dhc>(^rCsJ0J6nk4SSKy)Ok zO^R(|mk*KHh?IH7e=^T3B70q8JDEsaxPHa=@st@Pk_;k2MB660E~Q@PiXrVdjy5ZM zmtzzg7gN4Uj+0;6hig}297ZYr5_=>T;zRL))QO*EKFJ}jM~Q{xvz!SMYgzXha-Z!I zhmJ(zEqN&Zvm)Y4@tx?C_0m4&8S@Ke0U1I}WFnEX-~zeEev)VOzk(%YdtyPv7AZ@+ zIbn>IcNzLzdDh9_)ue_@B7Y(Dg<_Yi$Bb~iGKS-o`<)HzIev&%M>q{OV`)l}kP2f@U4v(J+ zFkid~=Epxod+S%wKBfoSH*P@twYSjzycIkgP3Vvjg${FnM2Alt(BZKO7ODlXF!X@M z+h(x%)C(5Bo`j{k8J45-VYzGxEWhzVN6)M1Snv)yzC8*ZkFSMQ7d@x%+ zB^sSwa?v?)4LXkxN9X@KfzGEL(WTQKbQydRUB(Yamkqno?+vB(jQOG@B)#sW^RgW;6<5u9eWgVXLwaJm}==U#8Y zIpY$XC;Gs7eIcALABKx_1zeKS;4*O@TsG~8%eQ}qtIJ-vraXb`lq9%rT@Tl*Z=##) zcj%VV2i;oMq1(YpaBJrSx5!m+Yx)Xqo5sQIrYE}hxrgq0ExIq&qxpqKC5zJu;`D$Lwo~}pF$O!ao zx{96~7og|$`RL_&4801rqSsrLUpb<;%@^pM@C3c5PD1ZJpQ87p0Qf{Uz-OEVd^WVe z=kj~#<9rl-hO9xK2}S6$;Q;zv^MkMbe)vY)!guWZ@Lg2^-?Mko*Y+v;CV8Omge~a% z$I<9}B?NwMIq*x_4Zl~C;J2|G{I0~HpIaLGrTd}ZYv<8#&wcd!$qD{`J>g$87yfU? z!~aMN{Ga?C{Z-ZIU%n0f7n;!j@Idr`yZ`|q9S~4ah=3(F2>ASc1oD0wIItZ8Yq}zE zWdQ`*Df9>hTF8w72B9JF&gLwleA+6T809-u~e z*$RX&JcRIL8bp}QKt%X8MAUqOh?V7tIP(n#*j>SZA&)TNm2()d`3?sB_&y@F-y?G3 z9z^b%gvf`>Fi;bPfpt|FxXB#@@77?D>M{mZzl}j3mSE7e$B1%oMpR}wqF(z9QTrDn z>QOABwGR+oU5)5g6{0U}#$e~|7@YbPgI^nu!Fz9D@O=|v0!AUGA{sI8O-Ibv5zyJ~ zf-dC%bTdk!+g}0QT{&GAIsu_cIm*o^o-D-mC; zLHv@bi2vG%1jiFdNO=PZ&1aCXsTc_t?jh0T5)!jxkofvrNc?0Fk}Q&ul;nq`*|tdf zd=8R3=OH<16_TepBYEFKBtI>{5bX*K8G8>yR$s-Czn(;jO<$zwZX;#fI;6BtLCS@4 z40XDNp((W(Iw>AQxu=I-`4*{edy$%AjnpZ>Aa&~~q+Z*KG&f76Wi}#hdNR_sk4M^d ze+=t64a4#_VA$-97`E>(81}dh>FSM0*RMqS;=dyO#GA;lsz64>_sFRALB{Ik$T&X( znGU;kCkHbuEg!+o3q! z2F24YQM@A&#dj8>q<1k&@>5YV_dApvUW<~4cQ7(&K1PYQk5A> z%Vwf=sl=#=$r#nx38S{?G3w4hR0Qruh4C0FR;o~Oej+M8 zY*9Jf7nL(UK; z3)TH+quO{9)hiNEeeMEkTvnqd`yy(lWua#4G}PSCqqa{M)Rx{r?c$G6%l$Ui(H>*d zcVX=8$r$_TY1Hw)T{pl6b=6lBd7OcF5s#!eLh%vS-@5V zy!FQVF_izi-3)x~;LokWR4bPz`?m2;O0CjTQC-K^3*a9#^JNDSt)*4GghSQVqG*b( zG1XQX8Vuf9#_EbHL%nxwO;welyunyg?LEAvDv)nM@NJFB2gH94EsEzNk_a<{uvys!=7V!eZ3n>X5J)U1+#Qty9N_ zhiG*gtvW>IuhOWsYPFWH!&v@LOzrsFYGmrPVVS8MH~Pgny@TZ!5~gb_=(I{9L=zVG zte{qDq!1FOic$JJF9d5765^FUT3w8)txsIU^8#nu^?NPMN=?^sT=W06kR~W|1&4>W z6+*Oa1zA9_PNNIe{7=QRzRw5!-U4mt-b&X|I=pC0j$TqaytSc5Wblhk&tmHx5vY=X z>i*stjmGjZ*+!GWTN@Y_$ZrpL|DSM&=C^?5BqYaGHZ;@@2nrfEZd_owUSAf-bPY1< zLxLJgD~#2nYE*%h4JO&V2`?HF5*rtirqyPsRcZ}$MjbLdB3vCGAESwn4OPX2#3qEt zB!q^?YSb|aabaP)SdBJLovx-9_f~x=Kf(NCnyZ6@Ls{ohURhi9Y-97IC@TQVw0-7^ zKdiqVRcBDPj8gydzn30(>4BFXcV=;z5Lpx1xB~gAk!0cvi3$#G`+JS6OG0jZK0GLO~DvOLk}G_ukITo1OO(0O(As zH}X<`GzNqE4S!!R3w#0J|1!IGgEMKC(S z*agNe_#buwfGa4;@*1AyS!r!qphLa=4!ewP4A>kE{%pqk*bdLfqtRN`-Or?;{-~vtysu% zRo!$?QJ1o)UsF`2QuHbhm+%*rk|XRbiv%Jzs8ow*!s7KIVMghjXb0%VAm0F5 zms*fv4|psn7^*j?sA{F&sFS^k522tCY#FbIn8r;abUa0g&cJS5Mh*T^4-8>XrZ6^v zdX~1sF|8%Ch?OCn!6WKyyeqM%|ZlPu;$+PJl(+eo+HL*6j|M>8j(ms@!jwP=@N!{9fUq)=xtqkC` zGnb#Q?^fPgVrFjo<>b{n7-mFw@5CoT`=osZ&$)Oa4h+M9fFICyVG8UPtIcY$+iW(6 z!`|xZaJ!sNm*3a!?daL9FydgA>CrDqvK=sDM!cqXPRlIV literal 0 HcmV?d00001 diff --git a/tests/taglib/main.rs b/tests/taglib/main.rs index 424ee197..5992ab69 100644 --- a/tests/taglib/main.rs +++ b/tests/taglib/main.rs @@ -8,5 +8,6 @@ mod test_flac; mod test_flacpicture; mod test_id3v1; mod test_info; +mod test_mp4; mod test_mpeg; mod test_speex; diff --git a/tests/taglib/test_mp4.rs b/tests/taglib/test_mp4.rs new file mode 100644 index 00000000..152a7a7b --- /dev/null +++ b/tests/taglib/test_mp4.rs @@ -0,0 +1,482 @@ +use crate::temp_file; +use std::borrow::Cow; +use std::io::{Read, Seek}; + +use lofty::mp4::{Atom, AtomData, AtomIdent, Ilst, Mp4Codec, Mp4File}; +use lofty::{Accessor, AudioFile, MimeType, ParseOptions, Picture, PictureType, TagExt, TagType}; + +#[test] +fn test_properties_aac() { + let mut file = temp_file!("tests/taglib/data/has-tags.m4a"); + let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.properties().duration().as_secs(), 3); + assert_eq!(f.properties().duration().as_millis(), 3708); + assert_eq!(f.properties().audio_bitrate(), 3); + assert_eq!(f.properties().channels(), 2); + assert_eq!(f.properties().sample_rate(), 44100); + assert_eq!(f.properties().bit_depth(), Some(16)); + // TODO: Check for encryption + // assert_eq!(f.properties().encrypted, false); + assert_eq!(f.properties().codec(), &Mp4Codec::AAC); +} + +#[test] +#[allow(clippy::needless_range_loop)] +fn test_properties_aac_without_bitrate() { + let mut file = temp_file!("tests/taglib/data/has-tags.m4a"); + let mut aac_data = Vec::new(); + file.read_to_end(&mut aac_data).unwrap(); + + assert!(aac_data.len() > 1960); + assert_eq!(&aac_data[1890..1894], b"mp4a"); + for i in 1956..1960 { + // Zero out the bitrate + aac_data[i] = 0; + } + + let f = Mp4File::read_from(&mut std::io::Cursor::new(aac_data), ParseOptions::new()).unwrap(); + assert_eq!(f.properties().duration().as_secs(), 3); + assert_eq!(f.properties().duration().as_millis(), 3708); + assert_eq!(f.properties().audio_bitrate(), 3); + assert_eq!(f.properties().channels(), 2); + assert_eq!(f.properties().sample_rate(), 44100); + assert_eq!(f.properties().bit_depth(), Some(16)); + // assert_eq!(f.properties().encrypted, false); + assert_eq!(f.properties().codec(), &Mp4Codec::AAC); +} + +#[test] +fn test_properties_alac() { + let mut file = temp_file!("tests/taglib/data/empty_alac.m4a"); + let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.properties().duration().as_secs(), 3); + assert_eq!(f.properties().duration().as_millis(), 3705); + assert_eq!(f.properties().audio_bitrate(), 3); + assert_eq!(f.properties().channels(), 2); + assert_eq!(f.properties().sample_rate(), 44100); + assert_eq!(f.properties().bit_depth(), Some(16)); + // assert_eq!(f.properties().encrypted, false); + assert_eq!(f.properties().codec(), &Mp4Codec::ALAC); +} + +#[test] +#[allow(clippy::needless_range_loop)] +fn test_properties_alac_without_bitrate() { + let mut file = temp_file!("tests/taglib/data/has-tags.m4a"); + let mut alac_data = Vec::new(); + file.read_to_end(&mut alac_data).unwrap(); + + assert!(alac_data.len() > 474); + assert_eq!(&alac_data[446..450], b"alac"); + for i in 470..474 { + // Zero out the bitrate + alac_data[i] = 0; + } + + let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.properties().duration().as_secs(), 3); + assert_eq!(f.properties().duration().as_millis(), 3705); + assert_eq!(f.properties().audio_bitrate(), 3); + assert_eq!(f.properties().channels(), 2); + assert_eq!(f.properties().sample_rate(), 44100); + assert_eq!(f.properties().bit_depth(), Some(16)); + // assert_eq!(f.properties().encrypted, false); + assert_eq!(f.properties().codec(), &Mp4Codec::ALAC); +} + +#[test] +fn test_properties_m4v() { + let mut file = temp_file!("tests/taglib/data/blank_video.m4v"); + let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.properties().duration().as_secs(), 0); + assert_eq!(f.properties().duration().as_millis(), 975); + assert_eq!(f.properties().audio_bitrate(), 96); + assert_eq!(f.properties().channels(), 2); + assert_eq!(f.properties().sample_rate(), 44100); + assert_eq!(f.properties().bit_depth(), Some(16)); + // assert_eq!(f.properties().encrypted, false); + assert_eq!(f.properties().codec(), &Mp4Codec::AAC); +} + +#[test] +fn test_check_valid() { + let mut file = temp_file!("tests/taglib/data/empty.aiff"); + assert!(Mp4File::read_from(&mut file, ParseOptions::new()).is_err()); +} + +#[test] +fn test_has_tag() { + { + let mut file = temp_file!("tests/taglib/data/has-tags.m4a"); + let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(f.ilst().is_some()); + } + + let mut file = temp_file!("tests/taglib/data/no-tags.m4a"); + + { + let mut f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert!(f.ilst().is_none()); + let mut tag = Ilst::default(); + tag.set_title(String::from("TITLE")); + f.set_ilst(tag); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(f.ilst().is_some()); + } +} + +#[test] +fn test_is_empty() { + let mut t1 = Ilst::default(); + assert!(t1.is_empty()); + t1.set_artist(String::from("Foo")); + assert!(!t1.is_empty()); +} + +#[test] +fn test_update_stco() { + // TODO: We don't update stco atoms +} + +#[test] +fn test_freeform() { + let mut file = temp_file!("tests/taglib/data/has-tags.m4a"); + + { + let mut f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert!(f.ilst().unwrap().contains(&AtomIdent::Freeform { + mean: Cow::Borrowed("com.apple.iTunes"), + name: Cow::Borrowed("iTunNORM"), + })); + + f.ilst_mut().unwrap().insert_atom(Atom::new( + AtomIdent::Freeform { + mean: Cow::Borrowed("org.kde.TagLib"), + name: Cow::Borrowed("Foo"), + }, + AtomData::UTF8(String::from("Bar")), + )); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + assert!(f.ilst().unwrap().contains(&AtomIdent::Freeform { + mean: Cow::Borrowed("org.kde.TagLib"), + name: Cow::Borrowed("Foo"), + })); + assert_eq!( + f.ilst() + .unwrap() + .atom(&AtomIdent::Freeform { + mean: Cow::Borrowed("org.kde.TagLib"), + name: Cow::Borrowed("Foo"), + }) + .unwrap() + .data() + .next(), + Some(&AtomData::UTF8(String::from("Bar"))) + ); + f.save_to(&mut file).unwrap(); + } +} + +#[test] +fn test_save_existing_when_ilst_is_last() { + let mut file = temp_file!("tests/taglib/data/ilst-is-last.m4a"); + + { + let mut f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let ilst = f.ilst_mut().unwrap(); + assert_eq!( + ilst.atom(&AtomIdent::Freeform { + mean: Cow::Borrowed("com.apple.iTunes"), + name: Cow::Borrowed("replaygain_track_minmax"), + }) + .unwrap() + .data() + .next() + .unwrap(), + &AtomData::UTF8(String::from("82,164")) + ); + assert_eq!(ilst.artist().as_deref(), Some("Pearl Jam")); + ilst.set_comment(String::from("foo")); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + let ilst = f.ilst().unwrap(); + + assert_eq!( + ilst.atom(&AtomIdent::Freeform { + mean: Cow::Borrowed("com.apple.iTunes"), + name: Cow::Borrowed("replaygain_track_minmax"), + }) + .unwrap() + .data() + .next() + .unwrap(), + &AtomData::UTF8(String::from("82,164")) + ); + assert_eq!(ilst.artist().as_deref(), Some("Pearl Jam")); + assert_eq!(ilst.comment().as_deref(), Some("foo")); + } +} + +#[test] +#[ignore] // TODO: Maybe? This just checks the moov atom's length. We don't retain any atoms we don't need. +fn test_64bit_atom() {} + +#[test] +fn test_gnre() { + let mut file = temp_file!("tests/taglib/data/gnre.m4a"); + let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.ilst().unwrap().genre().as_deref(), Some("Ska")); +} + +#[test] +fn test_covr_read() { + let mut file = temp_file!("tests/taglib/data/has-tags.m4a"); + let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + let tag = f.ilst().unwrap(); + assert!(tag.contains(&AtomIdent::Fourcc(*b"covr"))); + let mut covrs = tag.atom(&AtomIdent::Fourcc(*b"covr")).unwrap().data(); + let Some(AtomData::Picture(picture1)) = covrs.next() else { + unreachable!() + }; + let Some(AtomData::Picture(picture2)) = covrs.next() else { + unreachable!() + }; + + assert!(covrs.next().is_none()); + assert_eq!(picture1.mime_type(), &MimeType::Png); + assert_eq!(picture1.data().len(), 79); + assert_eq!(picture2.mime_type(), &MimeType::Jpeg); + assert_eq!(picture2.data().len(), 287); +} + +#[test] +fn test_covr_write() { + let mut file = temp_file!("tests/taglib/data/has-tags.m4a"); + + { + let mut f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let tag = f.ilst_mut().unwrap(); + assert!(tag.contains(&AtomIdent::Fourcc(*b"covr"))); + tag.insert_picture(Picture::new_unchecked( + PictureType::Other, + MimeType::Png, + None, + b"foo".to_vec(), + )); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + let tag = f.ilst().unwrap(); + assert!(tag.contains(&AtomIdent::Fourcc(*b"covr"))); + + let mut covrs = tag.atom(&AtomIdent::Fourcc(*b"covr")).unwrap().data(); + let Some(AtomData::Picture(picture1)) = covrs.next() else { + unreachable!() + }; + let Some(AtomData::Picture(picture2)) = covrs.next() else { + unreachable!() + }; + let Some(AtomData::Picture(picture3)) = covrs.next() else { + unreachable!() + }; + + assert!(covrs.next().is_none()); + assert_eq!(picture1.mime_type(), &MimeType::Png); + assert_eq!(picture1.data().len(), 79); + assert_eq!(picture2.mime_type(), &MimeType::Jpeg); + assert_eq!(picture2.data().len(), 287); + assert_eq!(picture3.mime_type(), &MimeType::Png); + assert_eq!(picture3.data().len(), 3); + } +} + +#[test] +fn test_covr_read2() { + let mut file = temp_file!("tests/taglib/data/covr-junk.m4a"); + let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + let tag = f.ilst().unwrap(); + assert!(tag.contains(&AtomIdent::Fourcc(*b"covr"))); + let mut covrs = tag.atom(&AtomIdent::Fourcc(*b"covr")).unwrap().data(); + let Some(AtomData::Picture(picture1)) = covrs.next() else { + unreachable!() + }; + let Some(AtomData::Picture(picture2)) = covrs.next() else { + unreachable!() + }; + + assert!(covrs.next().is_none()); + assert_eq!(picture1.mime_type(), &MimeType::Png); + assert_eq!(picture1.data().len(), 79); + assert_eq!(picture2.mime_type(), &MimeType::Jpeg); + assert_eq!(picture2.data().len(), 287); +} + +#[test] +#[ignore] +fn test_properties() { + // Marker test, Lofty does not replicate the properties API +} + +#[test] +#[ignore] +fn test_properties_all_supported() { + // Marker test, Lofty does not replicate the properties API +} + +#[test] +#[ignore] +fn test_properties_movement() { + // Marker test, Lofty does not replicate the properties API +} + +#[test] +fn test_fuzzed_file() { + let mut file = temp_file!("tests/taglib/data/infloop.m4a"); + let _ = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); +} + +#[test] +fn test_repeated_save() { + let mut file = temp_file!("tests/taglib/data/no-tags.m4a"); + let mut f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut tag = Ilst::default(); + tag.set_title(String::from("0123456789")); + f.set_ilst(tag); + + f.save_to(&mut file).unwrap(); + file.rewind().unwrap(); + f.save_to(&mut file).unwrap(); + file.rewind().unwrap(); + + let mut file_bytes = Vec::new(); + file.read_to_end(&mut file_bytes).unwrap(); + + assert_eq!( + file_bytes + .windows(10) + .position(|window| window == b"0123456789"), + Some(2862) + ); + assert_ne!(file_bytes.get(2863..2873), Some(b"0123456789".as_slice())); +} + +#[test] +fn test_with_zero_length_atom() { + let mut file = temp_file!("tests/taglib/data/infloop.m4a"); + let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.properties().duration().as_millis(), 1115); + assert_eq!(f.properties().sample_rate(), 22050); +} + +#[test] +#[ignore] +fn test_empty_values_remove_items() { + // Marker test, Lofty treats empty values as valid +} + +#[test] +fn test_remove_metadata() { + let mut file = temp_file!("tests/taglib/data/no-tags.m4a"); + + { + let mut f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert!(f.ilst().is_none()); + let mut tag = Ilst::default(); + assert!(tag.is_empty()); + tag.set_title(String::from("TITLE")); + f.set_ilst(tag); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert!(f.ilst().is_some()); + assert!(!f.ilst().unwrap().is_empty()); + TagType::MP4ilst.remove_from(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert!(f.ilst().is_none()); + + let mut original_file_bytes = Vec::new(); + let mut new_file_bytes = Vec::new(); + + let mut original_file = temp_file!("tests/taglib/data/no-tags.m4a"); + original_file.read_to_end(&mut original_file_bytes).unwrap(); + file.read_to_end(&mut new_file_bytes).unwrap(); + + // We need to do some editing, since we preserve the `udta` and `meta` atoms unlike TagLib + + // Remove the `udta` atom, which should be 53 bytes in length + new_file_bytes.splice(1505..1505 + 53, std::iter::empty()); + + // Fix the length of the `moov` atom + new_file_bytes[1500] = 0; + + assert_eq!(original_file_bytes, new_file_bytes); + } +} + +#[test] +fn test_non_full_meta_atom() { + let mut file = temp_file!("tests/taglib/data/non-full-meta.m4a"); + let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(f.ilst().is_some()); + + let tag = f.ilst().unwrap(); + assert!(tag.contains(&AtomIdent::Fourcc(*b"covr"))); + let mut covrs = tag.atom(&AtomIdent::Fourcc(*b"covr")).unwrap().data(); + let Some(AtomData::Picture(picture1)) = covrs.next() else { + unreachable!() + }; + let Some(AtomData::Picture(picture2)) = covrs.next() else { + unreachable!() + }; + + assert!(covrs.next().is_none()); + assert_eq!(picture1.mime_type(), &MimeType::Png); + assert_eq!(picture1.data().len(), 79); + assert_eq!(picture2.mime_type(), &MimeType::Jpeg); + assert_eq!(picture2.data().len(), 287); + + assert_eq!(tag.artist().as_deref(), Some("Test Artist!!!!")); + assert_eq!( + tag.atom(&AtomIdent::Fourcc(*b"\xa9too")) + .unwrap() + .data() + .next() + .unwrap(), + &AtomData::UTF8(String::from("FAAC 1.24")) + ); +} From 5b66f8ccfc98dba2b8cfebdd08919b53b9f419bc Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Tue, 14 Mar 2023 21:49:40 -0400 Subject: [PATCH 13/75] Tests: Add TagLib WavPack tests --- tests/taglib/data/dsd_stereo.wv | Bin 0 -> 52595 bytes tests/taglib/data/four_channels.wv | Bin 0 -> 53520 bytes tests/taglib/data/infloop.wv | Bin 0 -> 2462 bytes tests/taglib/data/no_length.wv | Bin 0 -> 532 bytes tests/taglib/data/non_standard_rate.wv | Bin 0 -> 132 bytes tests/taglib/data/tagged.wv | Bin 0 -> 76627 bytes tests/taglib/main.rs | 1 + tests/taglib/test_wavpack.rs | 156 +++++++++++++++++++++++++ 8 files changed, 157 insertions(+) create mode 100644 tests/taglib/data/dsd_stereo.wv create mode 100644 tests/taglib/data/four_channels.wv create mode 100644 tests/taglib/data/infloop.wv create mode 100644 tests/taglib/data/no_length.wv create mode 100644 tests/taglib/data/non_standard_rate.wv create mode 100644 tests/taglib/data/tagged.wv create mode 100644 tests/taglib/test_wavpack.rs diff --git a/tests/taglib/data/dsd_stereo.wv b/tests/taglib/data/dsd_stereo.wv new file mode 100644 index 0000000000000000000000000000000000000000..80619270fd5d5851887eef497f535ece8427fe56 GIT binary patch literal 52595 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%0D}~_LAV2(o?6A%P~bc#88C^ z3SJTO^5Cpf-=?GdG~nWKmA`y9-$7%#U9!E_oj69U#;>)tnsl|y`Rq;=n7Vja{j^Fy zBfzc}2LQ(qE$+sh&y=_AI(W10L|}Vrr5d{zU$eYp_0H5j^?;}~9Df3lJpWE(lSL0l z!m>s4cH7L9Ic|WtlCS}v{UK9g(R%WifQR(_KXe9b+pZ%jVCsx8`vGqlF>^lA*Br1V zG^r$y8Bs8Fc(l?qQ+R;~cz?UYuoLz!qwec#9)o@+t?q&q#%tvL&q?8HBSbec3n(a5 z<*%We2zM#Nklg+4dM#9i)N)4{18aE7+x&Zrwz-m4Int@()gtw`bMObHGnbRyj=2&-S52K0uu+~J`Yea5p4A?NR+V->B1svVX-o}|n zIkvpHa}8-~AGtkoevyzuD%Ey zEvtIj?qx!*rS^9RH{4)4+b;A6%L4+HRv`9j{n!3R zk2<&t*K}_L7)L{qWfcE_rzr`fvOW#$X<0`5u{Ah-!8^~2HF9-ATSjmRadUm(iGvla z>Y>)b_&p8po3oT+J*J~LmuDz zI%`oR6GZQqRr}Sg>)Cqk>=@KyH9vQ?cN>K)`}K+4(vX7Sw_6(fp{<-xJ-8NKmi!Gw zbkzRXCMAZGRt92PAYzQ>qY5Go(KWGO8a#`R^362a^J5~Mqav0Up*nL(385}6XE#Ab z^rFuuwk%9Rj0Wn^VsDBx;Alljv$d0;{^+h220q@c@pLIrFZ{Chp4In%@xqw${|LuPm&f)tC>UI$b=`X z+q8SgQd!b+&Y))m->CV-t1^X9K?vLT1e*5V5lgwrkgo`f5HumFE@ejeo9gl8^S#5% zO$E_!#Lf)gaqlZIzP({OVcaE$)y8j#pB;XDv@$bL_8qz#qkH2Rf#p0o_@c?>z>|Gt zc3jk1^W|!UzG1Lkz#?VX`VD#`>S|W#rHkd-8=2*J;9kc&VSw=vmlYijO9f|ISB{6}@Ih@dQ4!^R@bju3oYEX$d+ugSE4%KXRYq z5NN7KQls)iVU0a?7nJh@JB%E)3bWAczZ77=^H*Z*Gyp&$C)GLnZ?OLhNitlcOdfF6 zAeKK7B@|abx;M*F@BE%MgqZ8WAqINZALs#qobx%_f3Fq(I{^Sp{{RR&2pJdzFhQaH zoP-U7{epP^X~2;|K5plAKM!F;C$y`u1psrGz5s}ik1*0t p*v!vOSl0>jAChpilZ!V1bWZ&L!^X`C4FIXCs{RjZQUA;V{ttRSp{f7? literal 0 HcmV?d00001 diff --git a/tests/taglib/data/four_channels.wv b/tests/taglib/data/four_channels.wv new file mode 100644 index 0000000000000000000000000000000000000000..de682f242fdb7eb8792bc3d33b10361a3b6c731c GIT binary patch literal 53520 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Ҵ8( zn}|J)l0lWtvqN%Nl7u>vBr9(NcTb4iEJ#6{exOCa^^3v?T?V^{nu4q5$2NxF3r&u* zrDRVMPPoZ5O9Zz(vyBIM<}k+f^)8sg31jEaZfNOoq#}q3EcSif(I|;%OUyp5xwa;l zqMq!9?xB7Lf2-ac5@t6~QT5I-;0|dm!y$gW&q` z<7ahPq&BbpWWDRxU=b|v%mhsLl}%P=-ZE3(D)eBqNWSW_uHtQ37cdIflr zggON^VxL6-B=aP%jUAepp}EcK_nY0s^8wlE;-lOHZ+;X91tTp&>(or-)rxivY~y+B z1M|K2A?*wK1YTkHRDO6#%-W;|1Yk45EP+JzRL$E8X>@!5Bbr44=qej2CDCEXp-sP`movVh8@ zBCl$gMn1u%=;?Ik!XQgd6V;bigYVDgA6YjwK!1xwUSFep;5;T~FQC-iDK4Lj=$;Pj z86>}l2iHPY!fDV|y%Un@o}|tTu?8l9cWZFw2OMNsqp*K3W(;#Qyng)t?DKNyJ>{}} z{1G&9tqR-+Z@aO?&{nyqm3G9uleHdzcGx;atm*7=H-eF2Gz70CC^?679(LUJ*LU@itjQ#JbCqObr}NdbP{T8Z4U zEcS$>vE?QsSqXu5Ehd?1r;IvpSSDd2-69)e�>pUG+-CyH%x(PN!u9KA5nzmBeVI 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 Z3_louwluOe$j@hI_-7#bBkRHP{{U-F$SnW> literal 0 HcmV?d00001 diff --git a/tests/taglib/data/infloop.wv b/tests/taglib/data/infloop.wv new file mode 100644 index 0000000000000000000000000000000000000000..d8c720cfe06ede7b8275d051730591dc34bf3f26 GIT binary patch literal 2462 zcmXRfE68TZU|?WnVPN_!atrxq1w=I1FyJBB&C=t6>q2Qz3O{`Yh-RzUT?WZ3`zK>tI`M)g0?=@|YuO3f?6 z?f;_GR7bG?Ne7VPHB{?iOmn7`1=ncAOHX(ah;a{ literal 0 HcmV?d00001 diff --git a/tests/taglib/data/no_length.wv b/tests/taglib/data/no_length.wv new file mode 100644 index 0000000000000000000000000000000000000000..c06d1071d5ac76d9be9d6eff977a5f8cd4c82863 GIT binary patch literal 532 zcmXRfE6C1ZU|?WpVPNYZf+_+IT^wo!(7vHOB4iv zDi|4<7+lscFbHg9VqjqqU`R$tHroccd6NuFXB>(^Kh$sizX9DDd z>~pAN0^5abA2t(k=>@rfT)k;bjAZNWh#+6@s&(Y+J;TDpu*(J#H}CIsO5qHv`jVgM K;oRm(yj}nfOJeE( literal 0 HcmV?d00001 diff --git a/tests/taglib/data/non_standard_rate.wv b/tests/taglib/data/non_standard_rate.wv new file mode 100644 index 0000000000000000000000000000000000000000..ccc90277e3c0b92e4840f41b2d7057e4a5cf0101 GIT binary patch literal 132 zcmXRfE6A>4U|d7Hsho><19C>G@D<~G5Sfg#*6%rz~y zL_q+kj*)?h;RQ1T!vcN=1{MYZhLpsT#2la+CI)68pOp=47?en70;x=80&%q%x`6cm O`MUr0nbxeU-U0wcI2fn^ literal 0 HcmV?d00001 diff --git a/tests/taglib/data/tagged.wv b/tests/taglib/data/tagged.wv new file mode 100644 index 0000000000000000000000000000000000000000..333f8687177ea8d6b9075ef35e80c54869bc0aa0 GIT binary patch literal 76627 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 literal 0 HcmV?d00001 diff --git a/tests/taglib/main.rs b/tests/taglib/main.rs index 5992ab69..f331371a 100644 --- a/tests/taglib/main.rs +++ b/tests/taglib/main.rs @@ -11,3 +11,4 @@ mod test_info; mod test_mp4; mod test_mpeg; mod test_speex; +mod test_wavpack; diff --git a/tests/taglib/test_wavpack.rs b/tests/taglib/test_wavpack.rs new file mode 100644 index 00000000..b4fe13a9 --- /dev/null +++ b/tests/taglib/test_wavpack.rs @@ -0,0 +1,156 @@ +use crate::temp_file; + +use std::io::Seek; + +use lofty::ape::ApeTag; +use lofty::id3::v1::ID3v1Tag; +use lofty::wavpack::WavPackFile; +use lofty::{Accessor, AudioFile, ParseOptions}; + +#[test] +fn test_no_length_properties() { + let mut file = temp_file!("tests/taglib/data/no_length.wv"); + let f = WavPackFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.properties().duration().as_secs(), 3); + assert_eq!(f.properties().duration().as_millis(), 3705); + assert_eq!(f.properties().audio_bitrate(), 1); + assert_eq!(f.properties().channels(), 2); + assert_eq!(f.properties().bit_depth(), 16); + assert_eq!(f.properties().is_lossless(), true); + assert_eq!(f.properties().sample_rate(), 44100); + // TODO: CPPUNIT_ASSERT_EQUAL(163392U, f.audioProperties()->sampleFrames()); + assert_eq!(f.properties().version(), 1031); +} + +#[test] +fn test_multi_channel_properties() { + let mut file = temp_file!("tests/taglib/data/four_channels.wv"); + let f = WavPackFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.properties().duration().as_secs(), 3); + assert_eq!(f.properties().duration().as_millis(), 3833); + assert_eq!(f.properties().audio_bitrate(), 112); + assert_eq!(f.properties().channels(), 4); + assert_eq!(f.properties().bit_depth(), 16); + assert_eq!(f.properties().is_lossless(), false); + assert_eq!(f.properties().sample_rate(), 44100); + // TODO: CPPUNIT_ASSERT_EQUAL(169031U, f.audioProperties()->sampleFrames()); + assert_eq!(f.properties().version(), 1031); +} + +#[test] +fn test_dsd_stereo_properties() { + let mut file = temp_file!("tests/taglib/data/dsd_stereo.wv"); + let f = WavPackFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.properties().duration().as_secs(), 0); + assert_eq!(f.properties().duration().as_millis(), 200); + assert_eq!(f.properties().audio_bitrate(), 2096); + assert_eq!(f.properties().channels(), 2); + assert_eq!(f.properties().bit_depth(), 8); + assert_eq!(f.properties().is_lossless(), true); + assert_eq!(f.properties().sample_rate(), 352800); + // TODO: CPPUNIT_ASSERT_EQUAL(70560U, f.audioProperties()->sampleFrames()); + assert_eq!(f.properties().version(), 1040); +} + +#[test] +fn test_non_standard_rate_properties() { + let mut file = temp_file!("tests/taglib/data/non_standard_rate.wv"); + let f = WavPackFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.properties().duration().as_secs(), 3); + assert_eq!(f.properties().duration().as_millis(), 3675); + assert_eq!(f.properties().audio_bitrate(), 0); + assert_eq!(f.properties().channels(), 2); + assert_eq!(f.properties().bit_depth(), 16); + assert_eq!(f.properties().is_lossless(), true); + assert_eq!(f.properties().sample_rate(), 1000); + // TODO: CPPUNIT_ASSERT_EQUAL(3675U, f.audioProperties()->sampleFrames()); + assert_eq!(f.properties().version(), 1040); +} + +#[test] +fn test_tagged_properties() { + let mut file = temp_file!("tests/taglib/data/tagged.wv"); + let f = WavPackFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.properties().duration().as_secs(), 3); + assert_eq!(f.properties().duration().as_millis(), 3550); + assert_eq!(f.properties().audio_bitrate(), 172); + assert_eq!(f.properties().channels(), 2); + assert_eq!(f.properties().bit_depth(), 16); + assert_eq!(f.properties().is_lossless(), false); + assert_eq!(f.properties().sample_rate(), 44100); + // TODO: CPPUNIT_ASSERT_EQUAL(156556U, f.audioProperties()->sampleFrames()); + assert_eq!(f.properties().version(), 1031); +} + +#[test] +fn test_fuzzed_file() { + let mut file = temp_file!("tests/taglib/data/infloop.wv"); + let _ = WavPackFile::read_from(&mut file, ParseOptions::new()).unwrap(); +} + +#[test] +fn test_strip_and_properties() { + let mut file = temp_file!("tests/taglib/data/click.wv"); + + { + let mut f = WavPackFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut ape = ApeTag::default(); + ape.set_title(String::from("APE")); + f.set_ape(ape); + + let mut id3v1 = ID3v1Tag::default(); + id3v1.set_title(String::from("ID3v1")); + f.set_id3v1(id3v1); + + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + // NOTE: This is not the same as the TagLib test. + // Their test checks the first "TITLE" which changes when tags are stripped. + let mut f = WavPackFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.ape().unwrap().title().as_deref(), Some("APE")); + f.remove_ape(); + assert_eq!(f.id3v1().unwrap().title.as_deref(), Some("ID3v1")); + f.remove_id3v1(); + assert!(!f.contains_tag()); + } +} + +#[test] +fn test_repeated_save() { + let mut file = temp_file!("tests/taglib/data/click.wv"); + + { + let mut f = WavPackFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + assert!(f.ape().is_none()); + assert!(f.id3v1().is_none()); + + let mut ape = ApeTag::default(); + ape.set_title(String::from("01234 56789 ABCDE FGHIJ")); + f.set_ape(ape); + f.save_to(&mut file).unwrap(); + file.rewind().unwrap(); + + f.ape_mut().unwrap().set_title(String::from("0")); + f.save_to(&mut file).unwrap(); + file.rewind().unwrap(); + + let mut id3v1 = ID3v1Tag::default(); + id3v1.set_title(String::from("01234 56789 ABCDE FGHIJ")); + f.set_id3v1(id3v1); + f.ape_mut().unwrap().set_title(String::from( + "01234 56789 ABCDE FGHIJ 01234 56789 ABCDE FGHIJ 01234 56789", + )); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = WavPackFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(f.ape().is_some()); + assert!(f.id3v1().is_some()); + } +} From 9e263f9ead00934b87f43a996158084083758489 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Fri, 17 Mar 2023 12:14:04 -0400 Subject: [PATCH 14/75] Tests: Add TagLib Vorbis Comments tests --- tests/taglib/data/lowercase-fields.ogg | Bin 0 -> 4477 bytes tests/taglib/main.rs | 1 + tests/taglib/test_xiphcomment.rs | 165 +++++++++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 tests/taglib/data/lowercase-fields.ogg create mode 100644 tests/taglib/test_xiphcomment.rs diff --git a/tests/taglib/data/lowercase-fields.ogg b/tests/taglib/data/lowercase-fields.ogg new file mode 100644 index 0000000000000000000000000000000000000000..0ddd49357a906c70d6f3e5f222f0fa5404e4e2cb GIT binary patch literal 4477 zcmeHLeNatHl+?=l0qu3&|>lZIIRhca`BP{0# z{ezHojK%z(U=`3a9~62fJ@Qrimjm3>|NMSg5gyXC76eMnjddFenrej&=Bi>wzfdF0 zkjXM-8)fov5Qu}PvZ>i@Y2Iv5>kUE`WzgsiwZPqDYObuQY_5Evs^O*T`WG6_)y*wU zrp+oSB;BYQ8>_bGzF4ML?5)l#ev#&E*`C=@(?*8)MKs4qH+giUrW;i?-DvdZ4 z)|Zx0br_gxHa9nzLiPV0XbQJJCWSR zZ5L#0E1wWsdPKfH(ovqef2zlb=n1c=T6uP-iX*a%H(lf!TAxvO3td%2!vRO19JIJUw-$u`7XTF zC1&cvt3m0+C{r_->G=p17R9Ld@>VeOY7pY|WjXyNPJf+q(b3JZA2+eu^IEOp-6`;JTa2-11!VJb;{0I?}KdRYhwqZh817+ z&i9qZdbJ~pwY~XcZLYVSA1M}lEtBULdebL?8t5&J@q*l9@0q^R=n$1M(l+Il(7e5q zH+J@N$AMZu+}7iLZ49W0VRN^4>j+T!AGJ|$Z>4z_S{2jWJbOdX8&7u!fB%u^?Wwas z6^!5bvKMx8-7s-%n?s_&L$QAxI5^5*nXozK0ABR;s7 z(x!v}4?{|hsDnr4-*2iMq67S<3&gv9?OMsu;6PW#g!KK3Otb0sk@sVti%CC&ZFy$+ z45ih{ z>?wNIG@DtWf40I{yl1;-PxYyXUi9xW*+myK37!gKv3nymd$tWJC}~i_paxXmt*?ItN?3mmIFTw>*PG z-e)cZZl64Q>xb$}=78f6P4K)XJdeWF6c7x$))*RH!N=*ix=A`QBM3)zN;B%yE>r5udgBij-K<;}55PvD9v14s^l541-l$HzYG(I) zebq74ZU0EVq;~AX3+WyFv7aZs;x58|J%m>~RTtBIQ^fk=IRn|j^SxOuNuXjWa9#(( zysYbu0_CU_DuWC{J@Qpk*{hGqS1+tLp@sdW9 zks}^8=EvbtF7~J4`JM=!E$~M<%Vko{_L*h6hgje+pqdprkp>2)Tn9g*td)h?geo8rVZv)Khavv;xWkool8Y#1}yZT=5l5`%Mwy#{R>=!)~W=%!t6hFYzUy!XA`LLvM+|DD{i~TcHQ0Sv5BEfFZ zO2miV)NaCvQN)ltnv{qSQV3n$9}Z7PVcj_`60Sy8SuXw2em>d9tDaP(a4Bc{V(;tP z^aV(RG(;kN%@=)cCgq{DG*3bJnY_nMX(3a3UQ3AWld2TIdj|5Oc!UfL+K10Au-B;= zxG5Hj>8_^X2BbVVP0V7(^ffZ+N2cpd~d($EJbR}vsE&>4*tqf!RTnqX^w$uRwiaPZsB85lJhh@)|Myy;SmT zJ&=-{Ffv+XcT*gAD;_!iaVQ^-eAmbvolwmwYoP(kdXecqY$$s4VfR0x(jYlwGPFmU z2=otQ7P|MdtH(B-)=Zb|1dp<;aNJMv{d`LdN%##M@IbSntaF5Pq%Y#W&&nFkS|(Mg zqJwi)(nFS5k|53&R~Hd>iqfVXFX`0wA9hjN;majm=|jG`vd-c0sxA%j!DnTi>1)hp0*60`767+ug4lG8TAb40%(~ zvW8A^8eDt0K}U~O3X=4iNxE#j!8v3*vypttek_?KEwuX=upkhBOvFG8!( z9a!KF#N|$3tgNYPXl!nM*=l!m^+14xl89c0o2yuvlOwnA6O*14tbOWfp(uGB4q-g> sCxFn3*z8b7MMrQpo4Mt)PyH$s&*@t}P3`*azVd$7kG-Ji*dKTK7v71uJOBUy literal 0 HcmV?d00001 diff --git a/tests/taglib/main.rs b/tests/taglib/main.rs index f331371a..059bc646 100644 --- a/tests/taglib/main.rs +++ b/tests/taglib/main.rs @@ -12,3 +12,4 @@ mod test_mp4; mod test_mpeg; mod test_speex; mod test_wavpack; +mod test_xiphcomment; diff --git a/tests/taglib/test_xiphcomment.rs b/tests/taglib/test_xiphcomment.rs new file mode 100644 index 00000000..de126e8c --- /dev/null +++ b/tests/taglib/test_xiphcomment.rs @@ -0,0 +1,165 @@ +use crate::temp_file; +use lofty::ogg::{OggPictureStorage, VorbisComments, VorbisFile}; +use lofty::{ + Accessor, AudioFile, MimeType, ParseOptions, Picture, PictureInformation, PictureType, TagExt, +}; +use std::io::Seek; + +#[test] +fn test_year() { + let mut cmt = VorbisComments::default(); + assert_eq!(cmt.year(), None); + cmt.insert(String::from("YEAR"), String::from("2009"), false); + assert_eq!(cmt.year(), Some(2009)); + + // NOTE: Lofty will *always* prioritize "YEAR" over "DATE". TagLib doesn't have the same ideas, + // so we have to remove "YEAR". + let _ = cmt.remove("YEAR"); + + cmt.insert(String::from("DATE"), String::from("2008"), false); + assert_eq!(cmt.year(), Some(2008)); +} + +#[test] +fn test_set_year() { + let mut cmt = VorbisComments::default(); + cmt.insert(String::from("YEAR"), String::from("2009"), false); + cmt.insert(String::from("DATE"), String::from("2008"), false); + cmt.set_year(1995); + assert!(cmt.get("YEAR").is_none()); + assert_eq!(cmt.get("DATE"), Some("1995")); +} + +#[test] +fn test_track() { + let mut cmt = VorbisComments::default(); + assert_eq!(cmt.track(), None); + cmt.insert(String::from("TRACKNUM"), String::from("7"), false); + assert_eq!(cmt.track(), Some(7)); + cmt.insert(String::from("TRACKNUMBER"), String::from("8"), false); + assert_eq!(cmt.year(), Some(8)); +} + +#[test] +fn test_set_track() { + let mut cmt = VorbisComments::default(); + cmt.insert(String::from("TRACKNUM"), String::from("7"), false); + cmt.insert(String::from("TRACKNUMBER"), String::from("8"), false); + cmt.set_track(3); + assert!(cmt.get("TRACKNUM").is_none()); + assert_eq!(cmt.get("TRACKNUMBER"), Some("3")); +} + +#[test] +#[ignore] +fn test_invalid_keys1() { + // Marker test, Lofty does not replicate the properties API +} + +#[test] +fn test_invalid_keys2() { + let mut cmt = VorbisComments::default(); + cmt.insert(String::new(), String::new(), false); + cmt.insert(String::from("A=B"), String::new(), false); + cmt.insert(String::from("A~B"), String::new(), false); + cmt.insert(String::from("A\x7F"), String::new(), false); + cmt.insert(String::from("A\u{3456}"), String::new(), false); + + assert!(cmt.is_empty()); +} + +#[test] +fn test_clear_comment() { + let mut file = temp_file!("tests/taglib/data/empty.ogg"); + + { + let mut f = VorbisFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + f.vorbis_comments_mut() + .insert(String::from("COMMENT"), String::from("Comment1"), false); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let mut f = VorbisFile::read_from(&mut file, ParseOptions::new()).unwrap(); + f.vorbis_comments_mut().remove_comment(); + assert_eq!(f.vorbis_comments().comment(), None); + } +} + +#[test] +#[ignore] +fn test_remove_fields() { + // Market test, TagLib has some incredibly strange behavior in this test. + // + // When adding a field of the same key, TagLib will append each value to the same value. + // Meaning: + // + // tag.insert("title", "Title1", false); + // tag.insert("title, "Title2", false); + // assert_eq!(tag.title(), Some("Title1 Title2"); + // + // Lofty will never behave in this way. +} + +#[test] +fn test_picture() { + let mut file = temp_file!("tests/taglib/data/empty.ogg"); + + { + let mut f = VorbisFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let picture = Picture::new_unchecked( + PictureType::CoverBack, + MimeType::Jpeg, + Some(String::from("new image")), + b"JPEG data".to_vec(), + ); + let info = PictureInformation { + width: 5, + height: 6, + color_depth: 16, + num_colors: 7, + }; + + f.vorbis_comments_mut() + .insert_picture(picture, Some(info)) + .unwrap(); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = VorbisFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let pictures = f.vorbis_comments().pictures(); + assert_eq!(pictures.len(), 1); + assert_eq!(pictures[0].1.width, 5); + assert_eq!(pictures[0].1.height, 6); + assert_eq!(pictures[0].1.color_depth, 16); + assert_eq!(pictures[0].1.num_colors, 7); + assert_eq!(pictures[0].0.mime_type(), &MimeType::Jpeg); + assert_eq!(pictures[0].0.description(), Some("new image")); + assert_eq!(pictures[0].0.data(), b"JPEG data"); + } +} + +#[test] +fn test_lowercase_fields() { + let mut file = temp_file!("tests/taglib/data/lowercase-fields.ogg"); + + { + let f = VorbisFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert_eq!(f.vorbis_comments().title().as_deref(), Some("TEST TITLE")); + assert_eq!(f.vorbis_comments().artist().as_deref(), Some("TEST ARTIST")); + assert_eq!(f.vorbis_comments().pictures().len(), 1); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = VorbisFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(!f.vorbis_comments().pictures().is_empty()); + } +} From c8cf494d037f39af87aaf1a5491bb41641599b8b Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 18 Mar 2023 11:55:47 -0400 Subject: [PATCH 15/75] Tests: Update VorbisComments usages to use `push` --- tests/taglib/test_flac.rs | 117 +++++++++++-------------------- tests/taglib/test_xiphcomment.rs | 28 ++++---- 2 files changed, 56 insertions(+), 89 deletions(-) diff --git a/tests/taglib/test_flac.rs b/tests/taglib/test_flac.rs index 49fd4db8..1edc3755 100644 --- a/tests/taglib/test_flac.rs +++ b/tests/taglib/test_flac.rs @@ -264,16 +264,12 @@ fn test_save_multiple_values() { let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); - f.vorbis_comments_mut().unwrap().insert( - String::from("ARTIST"), - String::from("artist 1"), - false, - ); - f.vorbis_comments_mut().unwrap().insert( - String::from("ARTIST"), - String::from("artist 2"), - false, - ); + f.vorbis_comments_mut() + .unwrap() + .push(String::from("ARTIST"), String::from("artist 1")); + f.vorbis_comments_mut() + .unwrap() + .push(String::from("ARTIST"), String::from("artist 2")); f.save_to(&mut file).unwrap(); } file.rewind().unwrap(); @@ -295,99 +291,72 @@ fn test_dict() { #[test] fn test_properties() { let mut tag = VorbisComments::default(); - tag.insert(String::from("ALBUM"), String::from("Album"), false); - tag.insert( - String::from("ALBUMARTIST"), - String::from("Album Artist"), - false, - ); - tag.insert( + tag.push(String::from("ALBUM"), String::from("Album")); + tag.push(String::from("ALBUMARTIST"), String::from("Album Artist")); + tag.push( String::from("ALBUMARTISTSORT"), String::from("Album Artist Sort"), - false, - ); - tag.insert(String::from("ALBUMSORT"), String::from("Album Sort"), false); - tag.insert(String::from("ARTIST"), String::from("Artist"), false); - tag.insert(String::from("ARTISTS"), String::from("Artists"), false); - tag.insert( - String::from("ARTISTSORT"), - String::from("Artist Sort"), - false, ); - tag.insert(String::from("ASIN"), String::from("ASIN"), false); - tag.insert(String::from("BARCODE"), String::from("Barcode"), false); - tag.insert( + tag.push(String::from("ALBUMSORT"), String::from("Album Sort")); + tag.push(String::from("ARTIST"), String::from("Artist")); + tag.push(String::from("ARTISTS"), String::from("Artists")); + tag.push(String::from("ARTISTSORT"), String::from("Artist Sort")); + tag.push(String::from("ASIN"), String::from("ASIN")); + tag.push(String::from("BARCODE"), String::from("Barcode")); + tag.push( String::from("CATALOGNUMBER"), String::from("Catalog Number 1"), - false, ); - tag.insert( + tag.push( String::from("CATALOGNUMBER"), String::from("Catalog Number 2"), - false, ); - tag.insert(String::from("COMMENT"), String::from("Comment"), false); - tag.insert(String::from("DATE"), String::from("2021-01-10"), false); - tag.insert(String::from("DISCNUMBER"), String::from("3/5"), false); - tag.insert(String::from("GENRE"), String::from("Genre"), false); - tag.insert(String::from("ISRC"), String::from("UKAAA0500001"), false); - tag.insert(String::from("LABEL"), String::from("Label 1"), false); - tag.insert(String::from("LABEL"), String::from("Label 2"), false); - tag.insert(String::from("MEDIA"), String::from("Media"), false); - tag.insert( + tag.push(String::from("COMMENT"), String::from("Comment")); + tag.push(String::from("DATE"), String::from("2021-01-10")); + tag.push(String::from("DISCNUMBER"), String::from("3/5")); + tag.push(String::from("GENRE"), String::from("Genre")); + tag.push(String::from("ISRC"), String::from("UKAAA0500001")); + tag.push(String::from("LABEL"), String::from("Label 1")); + tag.push(String::from("LABEL"), String::from("Label 2")); + tag.push(String::from("MEDIA"), String::from("Media")); + tag.push( String::from("MUSICBRAINZ_ALBUMARTISTID"), String::from("MusicBrainz_AlbumartistID"), - false, ); - tag.insert( + tag.push( String::from("MUSICBRAINZ_ALBUMID"), String::from("MusicBrainz_AlbumID"), - false, ); - tag.insert( + tag.push( String::from("MUSICBRAINZ_ARTISTID"), String::from("MusicBrainz_ArtistID"), - false, ); - tag.insert( + tag.push( String::from("MUSICBRAINZ_RELEASEGROUPID"), String::from("MusicBrainz_ReleasegroupID"), - false, ); - tag.insert( + tag.push( String::from("MUSICBRAINZ_RELEASETRACKID"), String::from("MusicBrainz_ReleasetrackID"), - false, ); - tag.insert( + tag.push( String::from("MUSICBRAINZ_TRACKID"), String::from("MusicBrainz_TrackID"), - false, - ); - tag.insert( - String::from("ORIGINALDATE"), - String::from("2021-01-09"), - false, ); - tag.insert( + tag.push(String::from("ORIGINALDATE"), String::from("2021-01-09")); + tag.push( String::from("RELEASECOUNTRY"), String::from("Release Country"), - false, ); - tag.insert( + tag.push( String::from("RELEASESTATUS"), String::from("Release Status"), - false, ); - tag.insert( - String::from("RELEASETYPE"), - String::from("Release Type"), - false, - ); - tag.insert(String::from("SCRIPT"), String::from("Script"), false); - tag.insert(String::from("TITLE"), String::from("Title"), false); - tag.insert(String::from("TRACKNUMBER"), String::from("2"), false); - tag.insert(String::from("TRACKTOTAL"), String::from("4"), false); + tag.push(String::from("RELEASETYPE"), String::from("Release Type")); + tag.push(String::from("SCRIPT"), String::from("Script")); + tag.push(String::from("TITLE"), String::from("Title")); + tag.push(String::from("TRACKNUMBER"), String::from("2")); + tag.push(String::from("TRACKTOTAL"), String::from("4")); let mut file = temp_file!("tests/taglib/data/no-tags.flac"); { @@ -412,11 +381,9 @@ fn test_invalid() { let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); - f.vorbis_comments_mut().unwrap().insert( - String::from("H\x00c4\x00d6"), - String::from("bla"), - false, - ); + f.vorbis_comments_mut() + .unwrap() + .push(String::from("H\x00c4\x00d6"), String::from("bla")); } #[test] diff --git a/tests/taglib/test_xiphcomment.rs b/tests/taglib/test_xiphcomment.rs index de126e8c..a9061cb2 100644 --- a/tests/taglib/test_xiphcomment.rs +++ b/tests/taglib/test_xiphcomment.rs @@ -9,22 +9,22 @@ use std::io::Seek; fn test_year() { let mut cmt = VorbisComments::default(); assert_eq!(cmt.year(), None); - cmt.insert(String::from("YEAR"), String::from("2009"), false); + cmt.push(String::from("YEAR"), String::from("2009")); assert_eq!(cmt.year(), Some(2009)); // NOTE: Lofty will *always* prioritize "YEAR" over "DATE". TagLib doesn't have the same ideas, // so we have to remove "YEAR". let _ = cmt.remove("YEAR"); - cmt.insert(String::from("DATE"), String::from("2008"), false); + cmt.push(String::from("DATE"), String::from("2008")); assert_eq!(cmt.year(), Some(2008)); } #[test] fn test_set_year() { let mut cmt = VorbisComments::default(); - cmt.insert(String::from("YEAR"), String::from("2009"), false); - cmt.insert(String::from("DATE"), String::from("2008"), false); + cmt.push(String::from("YEAR"), String::from("2009")); + cmt.push(String::from("DATE"), String::from("2008")); cmt.set_year(1995); assert!(cmt.get("YEAR").is_none()); assert_eq!(cmt.get("DATE"), Some("1995")); @@ -34,17 +34,17 @@ fn test_set_year() { fn test_track() { let mut cmt = VorbisComments::default(); assert_eq!(cmt.track(), None); - cmt.insert(String::from("TRACKNUM"), String::from("7"), false); + cmt.push(String::from("TRACKNUM"), String::from("7")); assert_eq!(cmt.track(), Some(7)); - cmt.insert(String::from("TRACKNUMBER"), String::from("8"), false); + cmt.push(String::from("TRACKNUMBER"), String::from("8")); assert_eq!(cmt.year(), Some(8)); } #[test] fn test_set_track() { let mut cmt = VorbisComments::default(); - cmt.insert(String::from("TRACKNUM"), String::from("7"), false); - cmt.insert(String::from("TRACKNUMBER"), String::from("8"), false); + cmt.push(String::from("TRACKNUM"), String::from("7")); + cmt.push(String::from("TRACKNUMBER"), String::from("8")); cmt.set_track(3); assert!(cmt.get("TRACKNUM").is_none()); assert_eq!(cmt.get("TRACKNUMBER"), Some("3")); @@ -59,11 +59,11 @@ fn test_invalid_keys1() { #[test] fn test_invalid_keys2() { let mut cmt = VorbisComments::default(); - cmt.insert(String::new(), String::new(), false); - cmt.insert(String::from("A=B"), String::new(), false); - cmt.insert(String::from("A~B"), String::new(), false); - cmt.insert(String::from("A\x7F"), String::new(), false); - cmt.insert(String::from("A\u{3456}"), String::new(), false); + cmt.push(String::new(), String::new()); + cmt.push(String::from("A=B"), String::new()); + cmt.push(String::from("A~B"), String::new()); + cmt.push(String::from("A\x7F"), String::new()); + cmt.push(String::from("A\u{3456}"), String::new()); assert!(cmt.is_empty()); } @@ -77,7 +77,7 @@ fn test_clear_comment() { file.rewind().unwrap(); f.vorbis_comments_mut() - .insert(String::from("COMMENT"), String::from("Comment1"), false); + .push(String::from("COMMENT"), String::from("Comment1")); f.save_to(&mut file).unwrap(); } file.rewind().unwrap(); From 052731dc30357cf6b670c5edccf2397fdc2e74f2 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 18 Mar 2023 11:56:14 -0400 Subject: [PATCH 16/75] Tests: Add TagLib WAV tests --- tests/taglib/data/alaw.wav | Bin 0 -> 56858 bytes tests/taglib/data/duplicate_tags.wav | Bin 0 -> 17052 bytes tests/taglib/data/float64.wav | Bin 0 -> 68584 bytes tests/taglib/data/infloop.wav | Bin 0 -> 14272 bytes tests/taglib/data/pcm_with_fact_chunk.wav | Bin 0 -> 14756 bytes tests/taglib/data/segfault.wav | Bin 0 -> 30 bytes tests/taglib/data/uint8we.wav | Bin 0 -> 47240 bytes tests/taglib/data/zero-size-chunk.wav | Bin 0 -> 1024 bytes tests/taglib/main.rs | 1 + tests/taglib/test_wav.rs | 358 ++++++++++++++++++++++ 10 files changed, 359 insertions(+) create mode 100644 tests/taglib/data/alaw.wav create mode 100644 tests/taglib/data/duplicate_tags.wav create mode 100644 tests/taglib/data/float64.wav create mode 100644 tests/taglib/data/infloop.wav create mode 100644 tests/taglib/data/pcm_with_fact_chunk.wav create mode 100644 tests/taglib/data/segfault.wav create mode 100644 tests/taglib/data/uint8we.wav create mode 100644 tests/taglib/data/zero-size-chunk.wav create mode 100644 tests/taglib/test_wav.rs diff --git a/tests/taglib/data/alaw.wav b/tests/taglib/data/alaw.wav new file mode 100644 index 0000000000000000000000000000000000000000..cf548effcb450bf6a22521c2e22645597bec2d21 GIT binary patch literal 56858 zcmeI5%WIQq6vjgx!$4~nS`|ewrA@l6wfER`Mi^msGLo5fjkpkI7S_EZ+KRMf6E9Q| z8rq~=dr9p@Q~V>coMEIpyAitA$v4s|%wp*8KpqZn&88wBlKAvF&*i*dzMp=)a^?7+ zvDh!)|NO(vU+*N2$6~Q~tSR>0`B?1tOsxK=Z|i?^b1Zk~oBGd>zr|uV#_o*$^+zoB z`SaA&`g*xMSAUhuYipIt$B(V8nM^9xQGd0!r_;T?x!m;h*4Dzp?CjiJu~;gtu0DQz zE$B$Q6OQm8l`gaxJ|{rzoi_wT=Xv;WB!7Yl`Wd}L(rT_=;>-Dl2BOe`(!eX{xarKO39GiSQHlgYhL zHZl^A7Yd7u`|tYAoBQ|MPWJaFhxb0&=x9sJ_V&uk=4SNCqW|k^HJ=|J@9P^J+`oampt*-?`E-nBn5bI{osDr zCqMYS^69Uf`dz`iQs}vXAADX3{FP$=q2Px?KNNmff`?1};PXn-m*D*2)L-HKAkXIt zev}mY;gTN)eq@|eE$*1{#=p=F+W1-6L~&YocA%D zzry(=6g^k)gZI1KUn%rM(eFy~AojaH=aoXw75w1yisPZ+hXD_l{qEuQSKRL^<`w4$ z*AIoB^ZttaD+L|~ez^2khIxhapaKuxxAQ(xvX4^qiHh@Z++X2*E}2(>;KB8S^W&fW zxxf3PT+S<=2f5!>@PqfePW2qmE8e#&@`oXxbABlHAGlBCepfNCxW6*+Lx6{3UisvQ z0S|${GUz$Z9}+z9zSd_x=Xuble(-!=bJ<@>@}OdW9!UN$=tm%Vkn=;aKR56L>xa+$ z;Zr|29tQnzd7gpeq1d1Ed1c5Sf#^ptcqsTG@Vid+1M9gYe;i(ah4nlXJbdzl>$$>T z@&1bIc`*AypL)*oxy$~F^FuMOcwdtB*|+2U2j532@G$H@Fh6|y-5T~+PW1!hA<+-T zK1!e;q3F4xZ|8njkq2@9;QmT6ulPR7XJ5kk;dDQU^_=@Fg??~74+Rf{zY1htIlbS2 z`zrw+d|ny!Ly-sZyyAXWkv}*dhItjrc^?5kf;pdv^M_Ae*}XE@2>>30oy_?%Z552yQc z-tP+d!Tm1J=MCR~)Su_Y{chGL9(Z2yeXYV@@$;gFc_rWn-=90=NA$cRp3gZyTG@HdABsF^&<~e-&hxnekFWeV*z-9}{P4LSRP`kdeO_wRmvH^y`zV2)E9R9? z{owrYdH=b=UkUmWt{)r^r+Uu&U6*}3*K@_b)-bOy9+G*5^&`vkhfDo%m_JOsaES-+uO$8opQq;eTrsb>p7T7Y*bf@`f%_{39$Y`Tzf$;J zgTLZxY0J++P{^5lH^&C4YpH&%e&AiG$x?E9euQ;(_}s#k^AF52twWeN=<>ye*{jSAssV z;rS!-S6I(^f2HtOye|o5|H1Ka>aVbVDEPtk!*IUD<#{-#`5gNzL;g_oiHBA{4E%73 z2hZn{dByn=NT102cEx^BvX2tzhh+bu$RFJA`s4>bf5rW-BA@g8!S!5nz69gJ=aoc1 z4lh4&U*eM=PV*q<2cK6GeklBvLeF{r!2D3`KX^Vj+;1Sj!>}L3eTmO`iYWD*5YD5|McmtTTM*^0~as$^mKRk_BJ)0 zKK=gvg9ncut*pF$U8&^r489|C2|Q3M>7%YKS_mW?ecr?acs!5cq+L`kr3c7= z;3b*lO(4I;Fq;%U&yO$C%DrcwbsP;Pb@ZTfS2_)(o`%}2Rl182MoV(^PiE#_AHBO VlYdVn(%-eIz3-~ly39?j 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 literal 0 HcmV?d00001 diff --git a/tests/taglib/data/pcm_with_fact_chunk.wav b/tests/taglib/data/pcm_with_fact_chunk.wav new file mode 100644 index 0000000000000000000000000000000000000000..a6dc1d6c58bdbd08730cc16c71a940d581910765 GIT binary patch literal 14756 zcmeIup$)=N07cOUt*0ZfMr8#*X@VjI^be$}xIYep@fd^d!MXaXbD2wdkI8+yot9TS z4oP;|=i4QpL(*r+v$nc!j|dPTK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk q1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009F3EU?r^>o(^b literal 0 HcmV?d00001 diff --git a/tests/taglib/data/uint8we.wav b/tests/taglib/data/uint8we.wav new file mode 100644 index 0000000000000000000000000000000000000000..9623db2291a78e96fadbeeb7ca7e58d2daf89688 GIT binary patch literal 47240 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%0D$ezK;h2C#Esef zO7FuG&_6Ie(VboH5HhAG)YwgC+$$n%As<06>FO@69ap_`>lnDE{zl59)V5xocIu8O z5!1z8TOtLd|H7KFa6}DGBzCt40Vd%~;r7Yyugvr2JTDZpHi755%MtLV^ zy%OH=w7Nw&Ncch4TY@?D>Zxj;{HshJe$<# zh&0YOx|6I(hpk6;?8q`j+ud4wNv>gwV~qCDVSMCsXsm#4NqHG^YYa85$0YeM8fyYE zXVgOZ*vS@qHI>VV^99_XMXe2$a!V`QraMm(ZPh*@Cq^F0)Bw8aR{>qzT?weQ#oPL^ z#=0IqN>0dlEs2gg)88V7Db??GYS60sB=noHi@?2>y3m)>Bw*Jq#CULwc~>}P#(j0t zMp<-BN}C#cW48Vhaw_b7JW*}4haR-oVljfP&QU5&-gZZ*y)s%Ozw1-2jgcGak4S3h z_=skzizBXTyg|%s=rR9<1_}<;drVCd4t=Xs#!(uf!S_)EXOcR$C!Zrmw1-~PNC{Q` z)yMWiiYT8ULG~e9wFmatM?KFe!F7yg9~%z*>*$FsTC3K+u1YwUDeI0SwOAyUN0NKU zpS9$+{gl^U5mD2^+;ds&7P_LwOo_eVTG6IwHihH#4~2n7144bx+Gh6_4|Jzz^l(?U1Zo8cXeYAAJI(8*UVlRAw5sP`RA&a~ z6}79qv@Q7?(f=_RIp?Z5wN#WM$FjDM)`KN&E!v8ng-uY8+`4b>Rvj=t-QnvT?|O() zLS;^9S`O|fSGR;b)IAI9qNbtiC$?M9dP-3}ht%WE5o_vuL0fLpaYj`))$3N;75ilT z&^6K}D5)k;0+d4O7^9+YF{}dhsUtDuR(?AZX?KjEr*~MikM*1{JXhqj!j#6)ZMLW` z()RPAcCK3q%El-Nze$WDhH+oFM5MO@Pv&Q}pw?FQ!{!KmRBP@SZF`xF zv0Xpcyp+PBFGGUTo;%<=A2}{!hnuvq4|S_bob5Pg*lN7@yLvX2_qEi9_Kn^OCn#LY zQH{M6E9j|XL@GJ@Tv?101GJm1DJ{cps9Tj&d(KK{+uiFT->EZdzc+epZv%tS_*okH zj=|VF9pgs%ulpX_*4Ao_zsldQ@-}E4Sl4nDZ3e|{+1c4RniAoBtleslwPEb5R1CYh zt`qIoW8-Zui$U}@-N>p+DsuuA;Ve+v9j!ZCnfwwd}19nq+1AeG~w6J ztU>X*-yvUhyCJFjUF7w;wtbc3Z@277i`h(?2d62RC{s&SJ(hynF_%P{=y$Xq?;X!! zr&?NF5jsFkC!ZZ-p?8#HWkBs1EjvF5rH+#b!M3B_m?>?y?sqNa)(HrX4j(b5tQnSg z4JmM!dNs{#zn0T|>?hid`aTD}e|^tmW{_ihZClS-jufvNLdc5!jdr81?brOSTaGan zeYbTVA>q+mA=Fpk9eq@{zrEHIbPHNpqt?6jJ6e`r(Qb{ea#{DgZuh=?)!5f9M}MQ; zdhQe-@vd9mw_CTYL`KWeuGUJe(dr)8do7TZ8d;B(~L&om&xNdiEq3v((tt{kryjy!Vs}7<;UhC+mCFvIW>LWx8%p>E2%{kLSAe`nwvguU~Dm#`=Dl`h7r; z8d0U+~2&;Z%h7`0>7oeZz=G%K!Jyko2Nf~ z@ZiDkeWQ8$$-~y8uYc|D{m$?F?%(~L|Kjid?(cl z^HJmLUpsjC(Zj=k@cX~_PygXN@BH3_)5gh@#>1npf9>~APW!*t{^Z%ogTs^dC(YAG z4-XzSnqU9gH{SW$H@@}Y!8_ml_IKW)m2dvzZ~T*Q{{FZAoBx#ezp0&Xe)}8W`49Ph S5AvJ8{|DdskND>8w*Nm^KnY*~ literal 0 HcmV?d00001 diff --git a/tests/taglib/data/zero-size-chunk.wav b/tests/taglib/data/zero-size-chunk.wav new file mode 100644 index 0000000000000000000000000000000000000000..8517e797dc803bfbcea502588f70d68e643a6aad GIT binary patch literal 1024 zcmWIYbaUfiU| Date: Mon, 20 Mar 2023 08:47:31 -0400 Subject: [PATCH 17/75] Tests: Update TagLib MP4 tests for recent changes --- tests/taglib/test_mp4.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/taglib/test_mp4.rs b/tests/taglib/test_mp4.rs index 152a7a7b..058f4baf 100644 --- a/tests/taglib/test_mp4.rs +++ b/tests/taglib/test_mp4.rs @@ -157,7 +157,7 @@ fn test_freeform() { name: Cow::Borrowed("iTunNORM"), })); - f.ilst_mut().unwrap().insert_atom(Atom::new( + f.ilst_mut().unwrap().insert(Atom::new( AtomIdent::Freeform { mean: Cow::Borrowed("org.kde.TagLib"), name: Cow::Borrowed("Foo"), @@ -177,7 +177,7 @@ fn test_freeform() { assert_eq!( f.ilst() .unwrap() - .atom(&AtomIdent::Freeform { + .get(&AtomIdent::Freeform { mean: Cow::Borrowed("org.kde.TagLib"), name: Cow::Borrowed("Foo"), }) @@ -200,7 +200,7 @@ fn test_save_existing_when_ilst_is_last() { let ilst = f.ilst_mut().unwrap(); assert_eq!( - ilst.atom(&AtomIdent::Freeform { + ilst.get(&AtomIdent::Freeform { mean: Cow::Borrowed("com.apple.iTunes"), name: Cow::Borrowed("replaygain_track_minmax"), }) @@ -220,7 +220,7 @@ fn test_save_existing_when_ilst_is_last() { let ilst = f.ilst().unwrap(); assert_eq!( - ilst.atom(&AtomIdent::Freeform { + ilst.get(&AtomIdent::Freeform { mean: Cow::Borrowed("com.apple.iTunes"), name: Cow::Borrowed("replaygain_track_minmax"), }) @@ -252,7 +252,7 @@ fn test_covr_read() { let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); let tag = f.ilst().unwrap(); assert!(tag.contains(&AtomIdent::Fourcc(*b"covr"))); - let mut covrs = tag.atom(&AtomIdent::Fourcc(*b"covr")).unwrap().data(); + let mut covrs = tag.get(&AtomIdent::Fourcc(*b"covr")).unwrap().data(); let Some(AtomData::Picture(picture1)) = covrs.next() else { unreachable!() }; @@ -291,7 +291,7 @@ fn test_covr_write() { let tag = f.ilst().unwrap(); assert!(tag.contains(&AtomIdent::Fourcc(*b"covr"))); - let mut covrs = tag.atom(&AtomIdent::Fourcc(*b"covr")).unwrap().data(); + let mut covrs = tag.get(&AtomIdent::Fourcc(*b"covr")).unwrap().data(); let Some(AtomData::Picture(picture1)) = covrs.next() else { unreachable!() }; @@ -318,7 +318,7 @@ fn test_covr_read2() { let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); let tag = f.ilst().unwrap(); assert!(tag.contains(&AtomIdent::Fourcc(*b"covr"))); - let mut covrs = tag.atom(&AtomIdent::Fourcc(*b"covr")).unwrap().data(); + let mut covrs = tag.get(&AtomIdent::Fourcc(*b"covr")).unwrap().data(); let Some(AtomData::Picture(picture1)) = covrs.next() else { unreachable!() }; @@ -456,7 +456,7 @@ fn test_non_full_meta_atom() { let tag = f.ilst().unwrap(); assert!(tag.contains(&AtomIdent::Fourcc(*b"covr"))); - let mut covrs = tag.atom(&AtomIdent::Fourcc(*b"covr")).unwrap().data(); + let mut covrs = tag.get(&AtomIdent::Fourcc(*b"covr")).unwrap().data(); let Some(AtomData::Picture(picture1)) = covrs.next() else { unreachable!() }; @@ -472,7 +472,7 @@ fn test_non_full_meta_atom() { assert_eq!(tag.artist().as_deref(), Some("Test Artist!!!!")); assert_eq!( - tag.atom(&AtomIdent::Fourcc(*b"\xa9too")) + tag.get(&AtomIdent::Fourcc(*b"\xa9too")) .unwrap() .data() .next() From 61f3622fe548dfc9cea553f4c59cf289a56a08b2 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 20 Mar 2023 08:48:57 -0400 Subject: [PATCH 18/75] Tests: Add ignored TagLib OGA FLAC tests --- tests/taglib/data/segfault.oga | Bin 0 -> 120 bytes tests/taglib/main.rs | 3 ++- tests/taglib/test_ogaflac.rs | 45 +++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 tests/taglib/data/segfault.oga create mode 100644 tests/taglib/test_ogaflac.rs diff --git a/tests/taglib/data/segfault.oga b/tests/taglib/data/segfault.oga new file mode 100644 index 0000000000000000000000000000000000000000..e23c21706e2f87776b3b210be638f431e416c00e GIT binary patch literal 120 zcmeZIPY-5bVt|6`<8dhhK(-;${T;af=&?wZf>p|rsRR^Hyd>{Y-^*$$~ literal 0 HcmV?d00001 diff --git a/tests/taglib/main.rs b/tests/taglib/main.rs index c9c4c732..b431252b 100644 --- a/tests/taglib/main.rs +++ b/tests/taglib/main.rs @@ -10,7 +10,8 @@ mod test_id3v1; mod test_info; mod test_mp4; mod test_mpeg; +mod test_ogaflac; mod test_speex; +mod test_wav; mod test_wavpack; mod test_xiphcomment; -mod test_wav; diff --git a/tests/taglib/test_ogaflac.rs b/tests/taglib/test_ogaflac.rs new file mode 100644 index 00000000..80335ead --- /dev/null +++ b/tests/taglib/test_ogaflac.rs @@ -0,0 +1,45 @@ +use crate::temp_file; +use lofty::flac::FlacFile; +use lofty::ogg::VorbisComments; +use lofty::{Accessor, AudioFile, ParseOptions}; +use std::io::{Seek, SeekFrom}; + +#[test] +#[ignore] // TODO: We don't support FLAC in OGA +fn test_framing_bit() { + let mut file = temp_file!("tests/taglib/data/empty_flac.oga"); + + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut vorbis_comments = VorbisComments::new(); + vorbis_comments.set_artist(String::from("The Artist")); + f.set_vorbis_comments(vorbis_comments); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!( + f.vorbis_comments().unwrap().artist().as_deref(), + Some("The Artist") + ); + + assert_eq!(file.seek(SeekFrom::End(0)).unwrap(), 9134); + } +} + +#[test] +#[ignore] // TODO +fn test_fuzzed_file() { + let mut file = temp_file!("tests/taglib/data/segfault.oga"); + let f = FlacFile::read_from(&mut file, ParseOptions::new()); + assert!(f.is_err()); +} + +#[test] +#[ignore] +fn test_split_packets() { + // Marker test, Lofty does not retain the packet information +} From 4fcecd0d8c67e4d03f49f7cbc106cbe60b1ad283 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 20 Mar 2023 08:51:50 -0400 Subject: [PATCH 19/75] Tests: Fix track number check in TagLib XiphComment test --- tests/taglib/test_xiphcomment.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/taglib/test_xiphcomment.rs b/tests/taglib/test_xiphcomment.rs index a9061cb2..8f222661 100644 --- a/tests/taglib/test_xiphcomment.rs +++ b/tests/taglib/test_xiphcomment.rs @@ -37,7 +37,7 @@ fn test_track() { cmt.push(String::from("TRACKNUM"), String::from("7")); assert_eq!(cmt.track(), Some(7)); cmt.push(String::from("TRACKNUMBER"), String::from("8")); - assert_eq!(cmt.year(), Some(8)); + assert_eq!(cmt.track(), Some(8)); } #[test] From f9ff268bd0ac9744ead74cc29c9ab6b0181408d4 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Fri, 24 Mar 2023 18:08:26 -0400 Subject: [PATCH 20/75] Tests: Add TagLib Opus tests --- tests/taglib/main.rs | 1 + tests/taglib/test_opus.rs | 59 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 tests/taglib/test_opus.rs diff --git a/tests/taglib/main.rs b/tests/taglib/main.rs index b431252b..521f552b 100644 --- a/tests/taglib/main.rs +++ b/tests/taglib/main.rs @@ -15,3 +15,4 @@ mod test_speex; mod test_wav; mod test_wavpack; mod test_xiphcomment; +mod test_opus; diff --git a/tests/taglib/test_opus.rs b/tests/taglib/test_opus.rs new file mode 100644 index 00000000..3253c786 --- /dev/null +++ b/tests/taglib/test_opus.rs @@ -0,0 +1,59 @@ +use crate::temp_file; +use lofty::ogg::OpusFile; +use lofty::{Accessor, AudioFile, ParseOptions}; +use std::io::Seek; + +#[test] +fn test_audio_properties() { + let mut file = temp_file!("tests/taglib/data/correctness_gain_silent_output.opus"); + let f = OpusFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.properties().duration().as_secs(), 7); + assert_eq!(f.properties().duration().as_millis(), 7737); + assert_eq!(f.properties().audio_bitrate(), 36); + assert_eq!(f.properties().channels(), 1); + assert_eq!(f.properties().input_sample_rate(), 48000); + assert_eq!(f.properties().version(), 1); +} + +#[test] +fn test_read_comments() { + let mut file = temp_file!("tests/taglib/data/correctness_gain_silent_output.opus"); + let f = OpusFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!( + f.vorbis_comments().get("ENCODER"), + Some("Xiph.Org Opus testvectormaker") + ); + assert!(f.vorbis_comments().get("TESTDESCRIPTION").is_some()); + assert!(f.vorbis_comments().artist().is_none()); + assert_eq!(f.vorbis_comments().vendor(), "libopus 0.9.11-66-g64c2dd7"); +} + +#[test] +fn test_write_comments() { + let mut file = temp_file!("tests/taglib/data/correctness_gain_silent_output.opus"); + + { + let mut f = OpusFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + f.vorbis_comments_mut() + .set_artist(String::from("Your Tester")); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = OpusFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!( + f.vorbis_comments().get("ENCODER"), + Some("Xiph.Org Opus testvectormaker") + ); + assert!(f.vorbis_comments().get("TESTDESCRIPTION").is_some()); + assert_eq!(f.vorbis_comments().artist().as_deref(), Some("Your Tester")); + assert_eq!(f.vorbis_comments().vendor(), "libopus 0.9.11-66-g64c2dd7"); + } +} + +#[test] +#[ignore] +fn test_split_packets() { + // Marker test, Lofty does not retain packet information +} From 735a8d19937f2474742525f55306754a17295821 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 10 Apr 2023 09:15:29 -0400 Subject: [PATCH 21/75] Tests: Add TagLib OGG tests --- tests/taglib/main.rs | 3 +- tests/taglib/test_ogg.rs | 156 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 tests/taglib/test_ogg.rs diff --git a/tests/taglib/main.rs b/tests/taglib/main.rs index 521f552b..d4d2b966 100644 --- a/tests/taglib/main.rs +++ b/tests/taglib/main.rs @@ -11,8 +11,9 @@ mod test_info; mod test_mp4; mod test_mpeg; mod test_ogaflac; +mod test_ogg; +mod test_opus; mod test_speex; mod test_wav; mod test_wavpack; mod test_xiphcomment; -mod test_opus; diff --git a/tests/taglib/test_ogg.rs b/tests/taglib/test_ogg.rs new file mode 100644 index 00000000..5a86e319 --- /dev/null +++ b/tests/taglib/test_ogg.rs @@ -0,0 +1,156 @@ +use crate::temp_file; +use lofty::ogg::VorbisFile; +use lofty::{Accessor, AudioFile, ParseOptions}; + +use std::io::{Read, Seek, SeekFrom}; + +use byteorder::{LittleEndian, ReadBytesExt}; + +#[test] +fn test_simple() { + let mut file = temp_file!("tests/taglib/data/empty.ogg"); + + { + let mut f = VorbisFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + f.vorbis_comments_mut() + .set_artist(String::from("The Artist")); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = VorbisFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.vorbis_comments().artist().as_deref(), Some("The Artist")); + } +} + +#[test] +#[ignore] +fn test_split_packets1() { + // Marker test, Lofty doesn't retain packet information +} + +#[test] +fn test_split_packets2() { + let mut file = temp_file!("tests/taglib/data/empty.ogg"); + + let text = "X".repeat(60890); + { + let mut f = VorbisFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + f.vorbis_comments_mut().set_title(text.clone()); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let mut f = VorbisFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert_eq!(f.vorbis_comments().title().as_deref(), Some(&*text)); + + f.vorbis_comments_mut().set_title(String::from("ABCDE")); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = VorbisFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.vorbis_comments().title().as_deref(), Some("ABCDE")); + } +} + +#[test] +#[ignore] +fn test_dict_interface1() { + // Marker test, Lofty doesn't replicate the dictionary interface +} + +#[test] +#[ignore] +fn test_dict_interface2() { + // Marker test, Lofty doesn't replicate the dictionary interface +} + +#[test] +fn test_audio_properties() { + let mut file = temp_file!("tests/taglib/data/empty.ogg"); + let f = VorbisFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!(f.properties().duration().as_secs(), 3); + assert_eq!(f.properties().duration().as_millis(), 3685); + assert_eq!(f.properties().audio_bitrate(), 1); + assert_eq!(f.properties().channels(), 2); + assert_eq!(f.properties().sample_rate(), 44100); + assert_eq!(f.properties().version(), 0); + assert_eq!(f.properties().bitrate_max(), 0); + assert_eq!(f.properties().bitrate_nominal(), 112000); + assert_eq!(f.properties().bitrate_min(), 0); +} + +#[test] +fn test_page_checksum() { + let mut file = temp_file!("tests/taglib/data/empty.ogg"); + + { + let mut f = VorbisFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + f.vorbis_comments_mut() + .set_title(String::from("The Artist")); + f.save_to(&mut file).unwrap(); + + file.seek(SeekFrom::Start(0x50)).unwrap(); + assert_eq!(file.read_u32::().unwrap(), 0x3D3BD92D); + } + file.rewind().unwrap(); + { + let mut f = VorbisFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + f.vorbis_comments_mut() + .set_title(String::from("The Artist 2")); + f.save_to(&mut file).unwrap(); + + file.seek(SeekFrom::Start(0x50)).unwrap(); + assert_eq!(file.read_u32::().unwrap(), 0xD985291C); + } +} + +#[test] +fn test_page_granule_position() { + let mut file = temp_file!("tests/taglib/data/empty.ogg"); + + { + let mut f = VorbisFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + // Force the Vorbis comment packet to span more than one page and + // check if the granule position is -1 indicating that no packets + // finish on this page. + f.vorbis_comments_mut().set_comment("A".repeat(70000)); + f.save_to(&mut file).unwrap(); + + file.seek(SeekFrom::Start(0x3A)).unwrap(); + let mut buf = [0; 6]; + file.read_exact(&mut buf).unwrap(); + assert_eq!(buf, *b"OggS\0\0"); + assert_eq!(file.read_i64::().unwrap(), -1); + } + file.rewind().unwrap(); + { + let mut f = VorbisFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + // Use a small Vorbis comment package which ends on the seconds page and + // check if the granule position is zero. + f.vorbis_comments_mut() + .set_comment(String::from("A small comment")); + f.save_to(&mut file).unwrap(); + + file.seek(SeekFrom::Start(0x3A)).unwrap(); + let mut buf = [0; 6]; + file.read_exact(&mut buf).unwrap(); + assert_eq!(buf, *b"OggS\0\0"); + assert_eq!(file.read_i64::().unwrap(), 0); + } +} From 21d88d119706e0b47d5708fe64bdf1b20bb90f2c Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 10 Apr 2023 09:16:12 -0400 Subject: [PATCH 22/75] Tests: Fix TagLib TestFLAC::testSaveMultipleValues --- tests/taglib/test_flac.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/taglib/test_flac.rs b/tests/taglib/test_flac.rs index 1edc3755..d792d8cb 100644 --- a/tests/taglib/test_flac.rs +++ b/tests/taglib/test_flac.rs @@ -266,7 +266,7 @@ fn test_save_multiple_values() { f.vorbis_comments_mut() .unwrap() - .push(String::from("ARTIST"), String::from("artist 1")); + .insert(String::from("ARTIST"), String::from("artist 1")); f.vorbis_comments_mut() .unwrap() .push(String::from("ARTIST"), String::from("artist 2")); From 15ad04cdfa07af980f9e78a95769ac2e5e25fa4b Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 10 Apr 2023 15:30:31 -0400 Subject: [PATCH 23/75] Tests: Add TagLib ID3v2 tests --- tests/taglib/data/005411.id3 | Bin 0 -> 38402 bytes tests/taglib/data/broken-tenc.id3 | Bin 0 -> 400 bytes tests/taglib/data/compressed_id3_frame.mp3 | Bin 0 -> 5000 bytes tests/taglib/data/id3v22-tda.mp3 | Bin 0 -> 4096 bytes tests/taglib/data/unsynch.id3 | Bin 0 -> 320 bytes tests/taglib/data/w000.mp3 | Bin 0 -> 512 bytes tests/taglib/main.rs | 1 + tests/taglib/test_id3v2.rs | 814 +++++++++++++++++++++ 8 files changed, 815 insertions(+) create mode 100644 tests/taglib/data/005411.id3 create mode 100644 tests/taglib/data/broken-tenc.id3 create mode 100644 tests/taglib/data/compressed_id3_frame.mp3 create mode 100644 tests/taglib/data/id3v22-tda.mp3 create mode 100644 tests/taglib/data/unsynch.id3 create mode 100644 tests/taglib/data/w000.mp3 create mode 100644 tests/taglib/test_id3v2.rs diff --git a/tests/taglib/data/005411.id3 b/tests/taglib/data/005411.id3 new file mode 100644 index 0000000000000000000000000000000000000000..ab2e0997a0bcbf27aa169a09fd3f730d8319de9d GIT binary patch literal 38402 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 AdjJ3c literal 0 HcmV?d00001 diff --git a/tests/taglib/data/broken-tenc.id3 b/tests/taglib/data/broken-tenc.id3 new file mode 100644 index 0000000000000000000000000000000000000000..809040506f29b83c48e055251e164c807d3b2997 GIT binary patch literal 400 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 literal 0 HcmV?d00001 diff --git a/tests/taglib/data/w000.mp3 b/tests/taglib/data/w000.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f9c2261764526ff40aba618a1e9b9933acada5fd GIT binary patch literal 512 zcmdUqQA@)x6ore5`rwm4K<|s750-TE!M*9)BBSeCW@Pvz=}fz@G#PD5?Z5XFd=USE z2R@Q>a&K<3h^EILbb1qJX^PDTNK<)vV{1#9(F5I6-}nAu3@p6AwKcLxwXRV`=ae;M zOL9BaQmM2gtmDzvp%+~{RVXJveLl~}0Vg1sJn}GG{kYrd9gkNb+$H!mI5SISBwz3e z@(Yj{+bdJiyX|-yM@Y^9V-IXL<9QswT!UBHRLate&~^}pN}sr>8$pSbMi`#I%~5m? z*0v~=X*Y~9xGT@o-Cnf578>b(c#>bDK4>}q_ygkWFhSiB2>A1IyyefUko+%w1F}q5 ADgXcg literal 0 HcmV?d00001 diff --git a/tests/taglib/main.rs b/tests/taglib/main.rs index d4d2b966..25155b94 100644 --- a/tests/taglib/main.rs +++ b/tests/taglib/main.rs @@ -7,6 +7,7 @@ mod test_fileref; mod test_flac; mod test_flacpicture; mod test_id3v1; +mod test_id3v2; mod test_info; mod test_mp4; mod test_mpeg; diff --git a/tests/taglib/test_id3v2.rs b/tests/taglib/test_id3v2.rs new file mode 100644 index 00000000..e183348f --- /dev/null +++ b/tests/taglib/test_id3v2.rs @@ -0,0 +1,814 @@ +use crate::temp_file; +use lofty::id3::v2::{ + AttachedPictureFrame, CommentFrame, ExtendedTextFrame, ExtendedUrlFrame, Frame, FrameFlags, + FrameId, FrameValue, GEOBInformation, GeneralEncapsulatedObject, ID3v2Tag, ID3v2Version, + Popularimeter, SyncTextContentType, SyncTextInformation, SynchronizedText, TimestampFormat, + UniqueFileIdentifierFrame, UrlLinkFrame, +}; +use lofty::mpeg::MpegFile; +use lofty::{ + Accessor, AudioFile, MimeType, ParseOptions, Picture, PictureType, TagExt, TextEncoding, +}; +use std::borrow::Cow; +use std::io::Seek; + +#[test] +fn test_unsynch_decode() { + let mut file = temp_file!("tests/taglib/data/unsynch.id3"); + let f = MpegFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); + + assert!(f.id3v2().is_some()); + assert_eq!( + f.id3v2().unwrap().title().as_deref(), + Some("My babe just cares for me") + ); +} + +#[test] +#[ignore] // TODO: We don't support downgrading 2.4 tags to 2.3 +fn test_downgrade_utf8_for_id3v23_1() {} + +#[test] +#[ignore] // TODO: We don't support downgrading 2.4 tags to 2.3 +fn test_downgrade_utf8_for_id3v23_2() {} + +#[test] +#[ignore] // TODO: Need to think of a nice way to handle multiple UTF-16 values separated by null +fn test_utf16be_delimiter() {} + +#[test] +#[ignore] // TODO: Need to think of a nice way to handle multiple UTF-16 values separated by null +fn test_utf16_delimiter() {} + +#[test] +fn test_broken_frame1() { + let mut file = temp_file!("tests/taglib/data/broken-tenc.id3"); + let f = MpegFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); + + assert!(f + .id3v2() + .unwrap() + .contains(&FrameId::Valid(Cow::from("TENC")))); +} + +#[test] +#[ignore] +fn test_read_string_field() { + // Marker test, this is not an API Lofty replicates +} + +#[test] +fn test_parse_apic() { + let f = AttachedPictureFrame::parse( + b"\ + \x00\ + m\x00\ + \x01\ + d\x00\ + \x00", + ID3v2Version::V4, + ) + .unwrap(); + assert_eq!(f.picture.mime_type(), &MimeType::Unknown(String::from("m"))); + assert_eq!(f.picture.pic_type(), PictureType::Icon); + assert_eq!(f.picture.description(), Some("d")); +} + +#[test] +fn test_parse_apic_utf16_bom() { + let f = AttachedPictureFrame::parse( + b"\ + \x01\x69\x6d\x61\x67\x65\ + \x2f\x6a\x70\x65\x67\x00\x00\xfe\xff\x00\x63\x00\x6f\x00\x76\x00\ + \x65\x00\x72\x00\x2e\x00\x6a\x00\x70\x00\x67\x00\x00\xff\xd8\xff", + ID3v2Version::V4, + ) + .unwrap(); + + assert_eq!(f.picture.mime_type(), &MimeType::Jpeg); + assert_eq!(f.picture.pic_type(), PictureType::Other); + assert_eq!(f.picture.description(), Some("cover.jpg")); + assert_eq!(f.picture.data(), b"\xff\xd8\xff"); +} + +#[test] +fn test_parse_apicv22() { + let frame = AttachedPictureFrame::parse( + b"\ + \x00\ + JPG\ + \x01\ + d\x00\ + \x00", + ID3v2Version::V2, + ) + .unwrap(); + + assert_eq!(frame.picture.mime_type(), &MimeType::Jpeg); + assert_eq!(frame.picture.pic_type(), PictureType::Icon); + assert_eq!(frame.picture.description(), Some("d")); +} + +#[test] +fn test_render_apic() { + let f = AttachedPictureFrame { + encoding: TextEncoding::UTF8, + picture: Picture::new_unchecked( + PictureType::CoverBack, + MimeType::Png, + Some(String::from("Description")), + b"PNG data".to_vec(), + ), + }; + + assert_eq!( + f.as_bytes(ID3v2Version::V4).unwrap(), + b"\ + \x03\ + image/png\x00\ + \x04\ + Description\x00\ + PNG data" + ); +} + +#[test] +#[ignore] +fn test_dont_render22() { + // Marker test, not sure what's going on here? +} + +#[test] +fn test_parse_geob() { + let f = GeneralEncapsulatedObject::parse( + b"\ + \x00\ + m\x00\ + f\x00\ + d\x00\ + \x00", + ) + .unwrap(); + assert_eq!(f.information.mime_type.as_deref(), Some("m")); + assert_eq!(f.information.file_name.as_deref(), Some("f")); + assert_eq!(f.information.descriptor.as_deref(), Some("d")); +} + +#[test] +fn test_render_geob() { + let f = GeneralEncapsulatedObject { + information: GEOBInformation { + encoding: TextEncoding::Latin1, + mime_type: Some(String::from("application/octet-stream")), + file_name: Some(String::from("test.bin")), + descriptor: Some(String::from("Description")), + }, + data: vec![0x01; 3], + }; + + assert_eq!( + f.as_bytes(), + b"\ + \x00\ + application/octet-stream\x00\ + test.bin\x00\ + Description\x00\ + \x01\x01\x01" + ); +} + +#[test] +fn test_parse_popm() { + let f = Popularimeter::parse( + b"\ + email@example.com\x00\ + \x02\ + \x00\x00\x00\x03", + ) + .unwrap(); + assert_eq!(f.email, "email@example.com"); + assert_eq!(f.rating, 2); + assert_eq!(f.counter, 3); +} + +#[test] +fn test_parse_popm_without_counter() { + let f = Popularimeter::parse( + b"\ + email@example.com\x00\ + \x02", + ) + .unwrap(); + assert_eq!(f.email, "email@example.com"); + assert_eq!(f.rating, 2); + assert_eq!(f.counter, 0); +} + +#[test] +fn test_render_popm() { + let f = Popularimeter { + email: String::from("email@example.com"), + rating: 2, + counter: 3, + }; + + assert_eq!( + f.as_bytes(), + b"\ + email@example.com\x00\ + \x02\ + \x00\x00\x00\x03" + ); +} + +#[test] +#[ignore] +fn test_popm_to_string() { + // Marker test, Lofty doesn't have a display impl for Popularimeter +} + +#[test] +fn test_popm_from_file() { + let mut file = temp_file!("tests/taglib/data/xing.mp3"); + + let f = Popularimeter { + email: String::from("email@example.com"), + rating: 200, + counter: 3, + }; + + { + let mut foo = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut tag = ID3v2Tag::new(); + tag.insert( + Frame::new("POPM", FrameValue::Popularimeter(f), FrameFlags::default()).unwrap(), + ); + foo.set_id3v2(tag); + foo.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let bar = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let popm_frame = bar.id3v2().unwrap().get("POPM").unwrap(); + let popularimeter = match popm_frame.content() { + FrameValue::Popularimeter(popm) => popm, + _ => unreachable!(), + }; + + assert_eq!(popularimeter.email, "email@example.com"); + assert_eq!(popularimeter.rating, 200); + } +} + +#[test] +#[ignore] // TODO: We don't support RVA2 frames yet +fn test_parse_relative_volume_frame() {} + +#[test] +#[ignore] // TODO: We don't support RVA2 frames yet +fn test_render_relative_volume_frame() {} + +#[test] +fn test_parse_unique_file_identifier_frame() { + let f = UniqueFileIdentifierFrame::parse( + &mut &b"\ + owner\x00\ + \x00\x01\x02"[..], + ) + .unwrap() + .unwrap(); + + assert_eq!(f.owner, "owner"); + assert_eq!(f.identifier, &[0x00, 0x01, 0x02]); +} + +#[test] +fn test_parse_empty_unique_file_identifier_frame() { + let f = UniqueFileIdentifierFrame::parse( + &mut &b"\ + \x00\ + "[..], + ); + + // NOTE: TagLib considers a missing owner to be valid, we do not + assert!(f.is_err()); +} + +#[test] +fn test_render_unique_file_identifier_frame() { + let f = UniqueFileIdentifierFrame { + owner: String::from("owner"), + identifier: b"\x01\x02\x03".to_vec(), + }; + + assert_eq!( + f.as_bytes(), + b"\ +owner\x00\ +\x01\x02\x03" + ); +} + +#[test] +fn test_parse_url_link_frame() { + let f = UrlLinkFrame::parse(b"http://example.com").unwrap().unwrap(); + assert_eq!(f.url(), "http://example.com"); +} + +#[test] +fn test_render_url_link_frame() { + let f = UrlLinkFrame::parse(b"http://example.com").unwrap().unwrap(); + assert_eq!(f.as_bytes(), b"http://example.com"); +} + +#[test] +fn test_parse_user_url_link_frame() { + let f = ExtendedUrlFrame::parse( + b"\ + \x00\ + foo\x00\ + http://example.com", + ID3v2Version::V4, + ) + .unwrap() + .unwrap(); + + assert_eq!(f.description, String::from("foo")); + assert_eq!(f.content, String::from("http://example.com")); +} + +#[test] +fn test_render_user_url_link_frame() { + let f = ExtendedUrlFrame { + encoding: TextEncoding::Latin1, + description: String::from("foo"), + content: String::from("http://example.com"), + }; + + assert_eq!( + f.as_bytes(), + b"\ + \x00\ + foo\x00\ + http://example.com" + ); +} + +#[test] +#[ignore] // TODO: We don't support OWNE frames yet +fn test_parse_ownership_frame() {} + +#[test] +#[ignore] // TODO: We don't support OWNE frames yet +fn test_render_ownership_frame() {} + +#[test] +fn test_parse_synchronized_lyrics_frame() { + let f = SynchronizedText::parse( + b"\ + \x00\ +eng\ +\x02\ +\x01\ +foo\x00\ +Example\x00\ +\x00\x00\x04\xd2\ +Lyrics\x00\ +\x00\x00\x11\xd7", + ) + .unwrap(); + + assert_eq!(f.information.encoding, TextEncoding::Latin1); + assert_eq!(f.information.language, *b"eng"); + assert_eq!(f.information.timestamp_format, TimestampFormat::MS); + assert_eq!(f.information.content_type, SyncTextContentType::Lyrics); + assert_eq!(f.information.description.as_deref(), Some("foo")); + + assert_eq!(f.content.len(), 2); + assert_eq!(f.content[0].1, "Example"); + assert_eq!(f.content[0].0, 1234); + assert_eq!(f.content[1].1, "Lyrics"); + assert_eq!(f.content[1].0, 4567); +} + +#[test] +fn test_parse_synchronized_lyrics_frame_with_empty_description() { + let f = SynchronizedText::parse( + b"\ + \x00\ + eng\ + \x02\ + \x01\ + \x00\ + Example\x00\ + \x00\x00\x04\xd2\ + Lyrics\x00\ + \x00\x00\x11\xd7", + ) + .unwrap(); + + assert_eq!(f.information.encoding, TextEncoding::Latin1); + assert_eq!(f.information.language, *b"eng"); + assert_eq!(f.information.timestamp_format, TimestampFormat::MS); + assert_eq!(f.information.content_type, SyncTextContentType::Lyrics); + assert!(f.information.description.is_none()); + + assert_eq!(f.content.len(), 2); + assert_eq!(f.content[0].1, "Example"); + assert_eq!(f.content[0].0, 1234); + assert_eq!(f.content[1].1, "Lyrics"); + assert_eq!(f.content[1].0, 4567); +} + +#[test] +fn test_render_synchronized_lyrics_frame() { + let f = SynchronizedText { + information: SyncTextInformation { + encoding: TextEncoding::Latin1, + language: *b"eng", + timestamp_format: TimestampFormat::MS, + content_type: SyncTextContentType::Lyrics, + description: Some(String::from("foo")), + }, + content: vec![ + (1234, String::from("Example")), + (4567, String::from("Lyrics")), + ], + }; + + assert_eq!( + f.as_bytes().unwrap(), + b"\ + \x00\ + eng\ + \x02\ + \x01\ + foo\x00\ + Example\x00\ + \x00\x00\x04\xd2\ + Lyrics\x00\ + \x00\x00\x11\xd7" + ); +} + +#[test] +#[ignore] // TODO: We don't support ETCO frames yet +fn test_parse_event_timing_codes_frame() {} + +#[test] +#[ignore] // TODO: We don't support ETCO frames yet +fn test_render_event_timing_codes_frame() {} + +#[test] +fn test_parse_comments_frame() { + let f = CommentFrame::parse( + b"\x03\ + deu\ + Description\x00\ + Text", + ID3v2Version::V4, + ) + .unwrap() + .unwrap(); + + assert_eq!(f.encoding, TextEncoding::UTF8); + assert_eq!(f.language, *b"deu"); + assert_eq!(f.description, String::from("Description")); + assert_eq!(f.content, String::from("Text")); +} + +#[test] +fn test_render_comments_frame() { + let f = CommentFrame { + encoding: TextEncoding::UTF16, + language: *b"eng", + description: String::from("Description"), + content: String::from("Text"), + }; + + assert_eq!( + f.as_bytes().unwrap(), + b"\ + \x01\ + eng\ + \xff\xfeD\0e\0s\0c\0r\0i\0p\0t\0i\0o\0n\0\x00\x00\ + \xff\xfeT\0e\0x\0t\0" + ); +} + +#[test] +#[ignore] +fn test_parse_podcast_frame() { + // Marker test, Lofty doesn't have dedicated support for PCST frames, it seems unnecessary +} + +#[test] +#[ignore] +fn test_render_podcast_frame() { + // Marker test, Lofty doesn't have dedicated support for PCST frames, it seems unnecessary +} + +#[test] +#[ignore] // TODO: We don't support PRIV frames yet +fn test_parse_private_frame() {} + +#[test] +#[ignore] // TODO: We don't support PRIV frames yet +fn test_render_private_frame() {} + +#[test] +fn test_parse_user_text_identification_frame() { + let frame_without_description = ExtendedUrlFrame::parse( + b"\ + \x00\ + \x00\ + Text", + ID3v2Version::V4, + ) + .unwrap() + .unwrap(); + + assert_eq!(frame_without_description.description, String::new()); + assert_eq!(frame_without_description.content, String::from("Text")); + + let frame_with_description = ExtendedUrlFrame::parse( + b"\ + \x00\ + Description\x00\ + Text", + ID3v2Version::V4, + ) + .unwrap() + .unwrap(); + assert_eq!( + frame_with_description.description, + String::from("Description") + ); + assert_eq!(frame_with_description.content, String::from("Text")); +} + +#[test] +fn test_render_user_text_identification_frame() { + let mut f = ExtendedTextFrame { + encoding: TextEncoding::Latin1, + description: String::new(), + content: String::from("Text"), + }; + + assert_eq!( + f.as_bytes(), + b"\ + \x00\ + \x00\ + Text" + ); + + f.description = String::from("Description"); + + assert_eq!( + f.as_bytes(), + b"\ + \x00\ + Description\x00\ + Text" + ); +} + +#[test] +#[ignore] // TODO: iTunes, being the great application it is writes unsynchronized integers for sizes. There's no *great* way to detect this. +fn test_itunes_24_frame_size() { + let mut file = temp_file!("tests/taglib/data/005411.id3"); + let f = MpegFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); + + assert!(f + .id3v2() + .unwrap() + .contains(&FrameId::Valid(Cow::from("TIT2")))); + assert_eq!( + f.id3v2().unwrap().get_text("TIT2").unwrap(), + "Sunshine Superman" + ); +} + +#[test] +fn test_save_utf16_comment() { + let mut file = temp_file!("tests/taglib/data/xing.mp3"); + + // NOTE: You can change the default encoding in TagLib, Lofty does not support this + { + let mut foo = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut tag = ID3v2Tag::new(); + tag.insert( + Frame::new( + "COMM", + CommentFrame { + encoding: TextEncoding::UTF16, + language: *b"eng", + description: String::new(), + content: String::from("Test comment!"), + }, + FrameFlags::default(), + ) + .unwrap(), + ); + foo.set_id3v2(tag); + foo.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let bar = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!( + bar.id3v2().unwrap().comment().as_deref(), + Some("Test comment!") + ); + } +} + +#[test] +#[ignore] // TODO: We don't support downgrading to 2.3 tags yet +fn test_update_genre_23_1() {} + +#[test] +#[ignore] +fn test_update_genre23_2() { + // Marker test, Lofty doesn't do additional work with the genre string +} + +#[test] +#[ignore] +fn test_update_genre23_3() { + // Marker test, Lofty doesn't do additional work with the genre string +} + +#[test] +#[ignore] // TODO: We currently just return the genre string as it is in the tag, need to think about whether or not to convert numerical strings +fn test_update_genre_24() {} + +#[test] +fn test_update_date22() { + let mut file = temp_file!("tests/taglib/data/id3v22-tda.mp3"); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(f.id3v2().is_some()); + assert_eq!(f.id3v2().unwrap().year(), Some(2010)); +} + +#[test] +#[ignore] // TODO: Determine if this is even worth doing. It is just combining TYE+TDA when upgrading ID3v2.2 to 2.4 +fn test_update_full_date22() { + let mut file = temp_file!("tests/taglib/data/id3v22-tda.mp3"); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(f.id3v2().is_some()); + assert_eq!(f.id3v2().unwrap().get_text("TDRC").unwrap(), "2010-04-03"); +} + +#[test] +#[ignore] // TODO: We don't support downgrading 2.4 tags to 2.3 +fn test_downgrade_to_23() {} + +#[test] +fn test_compressed_frame_with_broken_length() { + let mut file = temp_file!("tests/taglib/data/compressed_id3_frame.mp3"); + let f = MpegFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); + assert!(f + .id3v2() + .unwrap() + .contains(&FrameId::Valid(Cow::from("APIC")))); + + let frame = f.id3v2().unwrap().get("APIC").unwrap(); + let picture = match frame.content() { + FrameValue::Picture(AttachedPictureFrame { picture, .. }) => picture, + _ => unreachable!(), + }; + + assert_eq!(picture.mime_type(), &MimeType::Bmp); + assert_eq!(picture.pic_type(), PictureType::Other); + assert!(picture.description().is_none()); + assert_eq!(picture.data().len(), 86414); +} + +#[test] +fn test_w000() { + let mut file = temp_file!("tests/taglib/data/w000.mp3"); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + + assert!(f + .id3v2() + .unwrap() + .contains(&FrameId::Valid(Cow::from("W000")))); + let frame = f.id3v2().unwrap().get("W000").unwrap(); + let url_frame = match frame.content() { + FrameValue::Url(url_frame) => url_frame, + _ => unreachable!(), + }; + assert_eq!(url_frame.url(), "lukas.lalinsky@example.com____"); +} + +#[test] +#[ignore] +fn test_property_interface() { + // Marker test, Lofty does not replicate the property interface +} + +#[test] +#[ignore] +fn test_property_interface2() { + // Marker test, Lofty does not replicate the property interface +} + +#[test] +#[ignore] +fn test_properties_movement() { + // Marker test, Lofty does not replicate the property interface. + // Outside of that, this is simply a text frame parsing test, which is redundant. +} + +#[test] +#[ignore] +fn test_property_grouping() { + // Marker test, Lofty does not replicate the property interface. + // Outside of that, this is simply a text frame parsing test, which is redundant. +} + +#[test] +fn test_delete_frame() { + let mut file = temp_file!("tests/taglib/data/rare_frames.mp3"); + + { + let mut f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let t = f.id3v2_mut().unwrap(); + t.remove("TCON"); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f2 = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let t = f2.id3v2().unwrap(); + assert!(!t.contains(&FrameId::Valid(Cow::from("TCON")))); + } +} + +#[test] +fn test_save_and_strip_id3v1_should_not_add_frame_from_id3v1_to_id3v2() { + let mut file = temp_file!("tests/taglib/data/xing.mp3"); + + { + let mut foo = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut tag = ID3v2Tag::new(); + tag.set_artist(String::from("Artist")); + foo.set_id3v2(tag); + foo.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let mut bar = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + bar.id3v2_mut().unwrap().remove("TPE1"); + + bar.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(f.id3v2().is_none()); +} + +#[test] +#[ignore] // TODO: We don't support CHAP frames yet +fn test_parse_chapter_frame() {} + +#[test] +#[ignore] // TODO: We don't support CHAP frames yet +fn test_render_chapter_frame() {} + +#[test] +#[ignore] // TODO: We don't support CTOC frames yet +fn test_parse_table_of_contents_frame() {} + +#[test] +#[ignore] // TODO: We don't support CTOC frames yet +fn test_render_table_of_contents_frame() {} + +#[test] +#[ignore] +fn test_empty_frame() { + // Marker test, Lofty will not remove empty frames, as they can be valid +} + +#[test] +#[ignore] +fn test_duplicate_tags() { + // Marker test, Lofty will combine duplicated tags +} + +#[test] +#[ignore] // TODO: We don't support CTOC frames yet +fn test_parse_toc_frame_with_many_children() {} From 226592e45b6418518ac961e269cb2bc37fba650e Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Fri, 21 Apr 2023 21:15:58 -0400 Subject: [PATCH 24/75] Tests: Update Taglib tests for new type names --- tests/taglib/test_aiff.rs | 24 ++++++++++----------- tests/taglib/test_ape.rs | 4 ++-- tests/taglib/test_fileref.rs | 24 ++++++++++----------- tests/taglib/test_id3v1.rs | 6 +++--- tests/taglib/test_mp4.rs | 2 +- tests/taglib/test_mpeg.rs | 42 ++++++++++++++++++------------------ tests/taglib/test_wav.rs | 8 +++---- 7 files changed, 55 insertions(+), 55 deletions(-) diff --git a/tests/taglib/test_aiff.rs b/tests/taglib/test_aiff.rs index 3808e3df..91439fb0 100644 --- a/tests/taglib/test_aiff.rs +++ b/tests/taglib/test_aiff.rs @@ -10,7 +10,7 @@ use crate::{assert_delta, temp_file}; fn test_aiff_properties() { let file = lofty::read_from_path("tests/taglib/data/empty.aiff").unwrap(); - assert_eq!(file.file_type(), FileType::AIFF); + assert_eq!(file.file_type(), FileType::Aiff); let properties = file.properties(); assert_eq!(properties.duration().as_secs(), 0); @@ -29,7 +29,7 @@ fn test_aiff_properties() { fn test_aifc_properties() { let file = lofty::read_from_path("tests/taglib/data/alaw.aifc").unwrap(); - assert_eq!(file.file_type(), FileType::AIFF); + assert_eq!(file.file_type(), FileType::Aiff); let properties = file.properties(); assert_eq!(properties.duration().as_secs(), 0); @@ -53,16 +53,16 @@ fn test_save_id3v2() { { let mut tfile = lofty::read_from(&mut file).unwrap(); - assert_eq!(tfile.file_type(), FileType::AIFF); + assert_eq!(tfile.file_type(), FileType::Aiff); - assert!(tfile.tag(lofty::TagType::ID3v2).is_none()); + assert!(tfile.tag(lofty::TagType::Id3v2).is_none()); - let mut tag = lofty::Tag::new(lofty::TagType::ID3v2); + let mut tag = lofty::Tag::new(lofty::TagType::Id3v2); tag.set_title("TitleXXX".to_string()); tfile.insert_tag(tag); file.rewind().unwrap(); tfile.save_to(&mut file).unwrap(); - assert!(tfile.contains_tag_type(lofty::TagType::ID3v2)); + assert!(tfile.contains_tag_type(lofty::TagType::Id3v2)); } file.rewind().unwrap(); @@ -70,15 +70,15 @@ fn test_save_id3v2() { { let mut tfile = lofty::read_from(&mut file).unwrap(); - assert_eq!(tfile.file_type(), FileType::AIFF); + assert_eq!(tfile.file_type(), FileType::Aiff); - let mut tag = tfile.tag(lofty::TagType::ID3v2).unwrap().to_owned(); + let mut tag = tfile.tag(lofty::TagType::Id3v2).unwrap().to_owned(); assert_eq!(tag.title().as_deref(), Some("TitleXXX")); tag.set_title("".to_string()); tfile.insert_tag(tag); file.rewind().unwrap(); tfile.save_to(&mut file).unwrap(); - assert!(!tfile.contains_tag_type(lofty::TagType::ID3v2)); + assert!(!tfile.contains_tag_type(lofty::TagType::Id3v2)); } file.rewind().unwrap(); @@ -86,9 +86,9 @@ fn test_save_id3v2() { { let tfile = lofty::read_from(&mut file).unwrap(); - assert_eq!(tfile.file_type(), FileType::AIFF); + assert_eq!(tfile.file_type(), FileType::Aiff); - assert!(!tfile.contains_tag_type(lofty::TagType::ID3v2)); + assert!(!tfile.contains_tag_type(lofty::TagType::Id3v2)); } } @@ -100,7 +100,7 @@ fn test_save_id3v2() { fn test_fuzzed_file1() { assert_eq!( get_filetype("tests/taglib/data/segfault.aif"), - FileType::AIFF + FileType::Aiff ); } diff --git a/tests/taglib/test_ape.rs b/tests/taglib/test_ape.rs index 49370f37..b7cc0ad0 100644 --- a/tests/taglib/test_ape.rs +++ b/tests/taglib/test_ape.rs @@ -80,13 +80,13 @@ fn test_properties_390() { fn test_fuzzed_file_1() { assert_eq!( get_filetype("tests/taglib/data/longloop.ape"), - FileType::APE + FileType::Ape ); } #[test] fn test_fuzzed_file_2() { - assert_eq!(get_filetype("tests/taglib/data/zerodiv.ape"), FileType::APE); + assert_eq!(get_filetype("tests/taglib/data/zerodiv.ape"), FileType::Ape); } #[test] diff --git a/tests/taglib/test_fileref.rs b/tests/taglib/test_fileref.rs index 8a0182e0..1bc62d3c 100644 --- a/tests/taglib/test_fileref.rs +++ b/tests/taglib/test_fileref.rs @@ -103,12 +103,12 @@ fn test_speex() { #[test] fn test_flac() { - file_ref_save("no-tags.flac", FileType::FLAC); + file_ref_save("no-tags.flac", FileType::Flac); } #[test] fn test_mp3() { - file_ref_save("xing.mp3", FileType::MPEG); + file_ref_save("xing.mp3", FileType::Mpeg); } #[test] @@ -120,35 +120,35 @@ fn test_true_audio() { #[test] fn test_mp4_1() { - file_ref_save("has-tags.m4a", FileType::MP4); + file_ref_save("has-tags.m4a", FileType::Mp4); } #[test] #[ignore] // TODO: The file has a malformed `free` atom. How does TagLib handle this? Currently we mess up entirely and just write a duplicate tag. fn test_mp4_2() { - file_ref_save("no-tags.m4a", FileType::MP4); + file_ref_save("no-tags.m4a", FileType::Mp4); } #[test] #[ignore] // TODO: We are able to write the first tag and even reread, but the second save causes a `SizeMismatch`. fn test_mp4_3() { - file_ref_save("no-tags.3g2", FileType::MP4); + file_ref_save("no-tags.3g2", FileType::Mp4); } #[test] fn test_mp4_4() { - file_ref_save("blank_video.m4v", FileType::MP4); + file_ref_save("blank_video.m4v", FileType::Mp4); } #[test] fn test_wav() { - file_ref_save("empty.wav", FileType::WAV); + file_ref_save("empty.wav", FileType::Wav); } #[test] #[ignore] // TODO: We don't yet support FLAC in oga fn test_oga_flac() { - file_ref_save("empty_flac.oga", FileType::FLAC); + file_ref_save("empty_flac.oga", FileType::Flac); } #[test] @@ -158,17 +158,17 @@ fn test_oga_vorbis() { #[test] fn test_ape() { - file_ref_save("mac-399.ape", FileType::APE); + file_ref_save("mac-399.ape", FileType::Ape); } #[test] fn test_aiff_1() { - file_ref_save("empty.aiff", FileType::AIFF); + file_ref_save("empty.aiff", FileType::Aiff); } #[test] fn test_aiff_2() { - file_ref_save("alaw.aifc", FileType::AIFF); + file_ref_save("alaw.aifc", FileType::Aiff); } #[test] @@ -222,7 +222,7 @@ fn test_default_file_extensions() { fn test_file_resolver() { { let file = lofty::read_from_path("tests/taglib/data/xing.mp3").unwrap(); - assert_eq!(file.file_type(), FileType::MPEG); + assert_eq!(file.file_type(), FileType::Mpeg); } struct DummyResolver; diff --git a/tests/taglib/test_id3v1.rs b/tests/taglib/test_id3v1.rs index 8cc6bed4..61f428a6 100644 --- a/tests/taglib/test_id3v1.rs +++ b/tests/taglib/test_id3v1.rs @@ -3,7 +3,7 @@ use crate::temp_file; use std::io::Seek; use lofty::id3::v1::{ID3v1Tag, GENRES}; -use lofty::mpeg::MPEGFile; +use lofty::mpeg::MpegFile; use lofty::{Accessor, AudioFile, ParseOptions}; #[test] @@ -11,7 +11,7 @@ use lofty::{Accessor, AudioFile, ParseOptions}; fn test_strip_whitespace() { let mut file = temp_file!("tests/taglib/data/xing.mp3"); { - let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let mut f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); let mut tag = ID3v1Tag::default(); @@ -21,7 +21,7 @@ fn test_strip_whitespace() { } file.rewind().unwrap(); { - let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert_eq!(f.id3v1().unwrap().artist().as_deref(), Some("Artist")); } } diff --git a/tests/taglib/test_mp4.rs b/tests/taglib/test_mp4.rs index 058f4baf..c6cab2b1 100644 --- a/tests/taglib/test_mp4.rs +++ b/tests/taglib/test_mp4.rs @@ -420,7 +420,7 @@ fn test_remove_metadata() { assert!(f.ilst().is_some()); assert!(!f.ilst().unwrap().is_empty()); - TagType::MP4ilst.remove_from(&mut file).unwrap(); + TagType::Mp4Ilst.remove_from(&mut file).unwrap(); } file.rewind().unwrap(); { diff --git a/tests/taglib/test_mpeg.rs b/tests/taglib/test_mpeg.rs index e88c47a6..afeb1a57 100644 --- a/tests/taglib/test_mpeg.rs +++ b/tests/taglib/test_mpeg.rs @@ -6,14 +6,14 @@ use std::io::Seek; use lofty::ape::ApeTag; use lofty::id3::v1::ID3v1Tag; use lofty::id3::v2::{ID3v2Tag, ID3v2Version}; -use lofty::mpeg::MPEGFile; +use lofty::mpeg::MpegFile; use lofty::{Accessor, AudioFile, ParseOptions}; #[test] #[ignore] fn test_audio_properties_xing_header_cbr() { let mut file = File::open("tests/taglib/data/lame_cbr.mp3").unwrap(); - let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert_eq!(f.properties().duration().as_secs(), 1887); // TODO: Off by 9 assert_eq!(f.properties().duration().as_millis(), 1887164); @@ -28,7 +28,7 @@ fn test_audio_properties_xing_header_cbr() { #[ignore] fn test_audio_properties_xing_header_vbr() { let mut file = File::open("tests/taglib/data/lame_vbr.mp3").unwrap(); - let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert_eq!(f.properties().duration().as_secs(), 1887); // TODO: Off by 9 assert_eq!(f.properties().duration().as_millis(), 1887164); @@ -43,7 +43,7 @@ fn test_audio_properties_xing_header_vbr() { #[ignore] fn test_audio_properties_vbri_header() { let mut file = File::open("tests/taglib/data/rare_frames.mp3").unwrap(); - let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert_eq!(f.properties().duration().as_secs(), 222); // TODO: Off by 1 assert_eq!(f.properties().duration().as_millis(), 222198); @@ -58,7 +58,7 @@ fn test_audio_properties_vbri_header() { #[ignore] fn test_audio_properties_no_vbr_headers() { let mut file = File::open("tests/taglib/data/bladeenc.mp3").unwrap(); - let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert_eq!(f.properties().duration().as_secs(), 3); // Off by 1 assert_eq!(f.properties().duration().as_millis(), 3553); @@ -73,7 +73,7 @@ fn test_audio_properties_no_vbr_headers() { #[test] fn test_skip_invalid_frames_1() { let mut file = File::open("tests/taglib/data/invalid-frames1.mp3").unwrap(); - let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert_eq!(f.properties().duration().as_secs(), 0); assert_eq!(f.properties().duration().as_millis(), 392); @@ -86,7 +86,7 @@ fn test_skip_invalid_frames_1() { #[ignore] fn test_skip_invalid_frames_2() { let mut file = File::open("tests/taglib/data/invalid-frames2.mp3").unwrap(); - let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert_eq!(f.properties().duration().as_secs(), 0); assert_eq!(f.properties().duration().as_millis(), 314); // TODO: Off by 79 @@ -99,7 +99,7 @@ fn test_skip_invalid_frames_2() { #[ignore] fn test_skip_invalid_frames_3() { let mut file = File::open("tests/taglib/data/invalid-frames3.mp3").unwrap(); - let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert_eq!(f.properties().duration().as_secs(), 0); assert_eq!(f.properties().duration().as_millis(), 183); // TODO: Off by 26 @@ -112,7 +112,7 @@ fn test_skip_invalid_frames_3() { #[ignore] fn test_version_2_duration_with_xing_header() { let mut file = File::open("tests/taglib/data/mpeg2.mp3").unwrap(); - let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert_eq!(f.properties().duration().as_secs(), 5387); // TODO: Off by 15 assert_eq!(f.properties().duration().as_millis(), 5387285); } @@ -123,7 +123,7 @@ fn test_save_id3v24() { let xxx = "X".repeat(254); { - let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let mut f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); assert!(f.id3v2().is_none()); @@ -135,7 +135,7 @@ fn test_save_id3v24() { } file.rewind().unwrap(); { - let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert_eq!(f.id3v2().unwrap().original_version(), ID3v2Version::V4); assert_eq!(f.id3v2().unwrap().artist().as_deref(), Some("Artist A")); assert_eq!(f.id3v2().unwrap().title().as_deref(), Some(xxx.as_str())); @@ -155,7 +155,7 @@ fn test_save_id3v23() { let xxx = "X".repeat(254); { - let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let mut f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); assert!(f.id3v2().is_none()); @@ -167,7 +167,7 @@ fn test_save_id3v23() { } file.rewind().unwrap(); { - let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert_eq!(f.id3v2().unwrap().original_version(), ID3v2Version::V3); assert_eq!(f.id3v2().unwrap().artist().as_deref(), Some("Artist A")); assert_eq!(f.id3v2().unwrap().title().as_deref(), Some(xxx.as_str())); @@ -177,14 +177,14 @@ fn test_save_id3v23() { #[test] fn test_duplicate_id3v2() { let mut file = File::open("tests/taglib/data/duplicate_id3v2.mp3").unwrap(); - let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert_eq!(f.properties().sample_rate(), 44100); } #[test] fn test_fuzzed_file() { let mut file = File::open("tests/taglib/data/invalid-frames3.mp3").unwrap(); - let _ = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let _ = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); } #[test] @@ -198,7 +198,7 @@ fn test_strip_and_properties() { let mut file = temp_file!("tests/taglib/data/xing.mp3"); { - let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let mut f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); let mut id3v2 = ID3v2Tag::default(); @@ -214,7 +214,7 @@ fn test_strip_and_properties() { } file.rewind().unwrap(); { - let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let mut f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert_eq!(f.id3v2().unwrap().title().as_deref(), Some("ID3v2")); f.remove_id3v2(); assert_eq!(f.ape().unwrap().title().as_deref(), Some("APE")); @@ -245,7 +245,7 @@ fn test_repeated_save_3() { let mut file = temp_file!("tests/taglib/data/xing.mp3"); { - let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let mut f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); assert!(f.ape().is_none()); assert!(f.id3v1().is_none()); @@ -276,7 +276,7 @@ fn test_repeated_save_3() { } file.rewind().unwrap(); { - let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert!(f.ape().is_some()); assert!(f.id3v1().is_some()); } @@ -306,7 +306,7 @@ fn test_ignore_garbage() { let mut file = temp_file!("tests/taglib/data/garbage.mp3"); { - let mut f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let mut f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); assert!(f.id3v2().is_some()); @@ -316,7 +316,7 @@ fn test_ignore_garbage() { } file.rewind().unwrap(); { - let f = MPEGFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert!(f.id3v2().is_some()); assert_eq!(f.id3v2().unwrap().title().as_deref(), Some("Title B")); } diff --git a/tests/taglib/test_wav.rs b/tests/taglib/test_wav.rs index 3568a452..e0f4f44c 100644 --- a/tests/taglib/test_wav.rs +++ b/tests/taglib/test_wav.rs @@ -193,7 +193,7 @@ fn test_strip_tags() { assert!(f.id3v2().is_some()); assert!(f.riff_info().is_some()); - TagType::RIFFInfo.remove_from(&mut file).unwrap(); + TagType::RiffInfo.remove_from(&mut file).unwrap(); } file.rewind().unwrap(); { @@ -215,7 +215,7 @@ fn test_strip_tags() { assert!(f.id3v2().is_some()); assert!(f.riff_info().is_some()); - TagType::ID3v2.remove_from(&mut file).unwrap(); + TagType::Id3v2.remove_from(&mut file).unwrap(); } file.rewind().unwrap(); { @@ -311,9 +311,9 @@ fn test_file_with_garbage_appended() { } file.rewind().unwrap(); { - TagType::ID3v2.remove_from(&mut file).unwrap(); + TagType::Id3v2.remove_from(&mut file).unwrap(); file.rewind().unwrap(); - TagType::RIFFInfo.remove_from(&mut file).unwrap(); + TagType::RiffInfo.remove_from(&mut file).unwrap(); } file.rewind().unwrap(); { From e9f1fddb56b0534189b5ea133f98bf6d7acfa2fc Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 22 Apr 2023 21:59:18 -0400 Subject: [PATCH 25/75] Tests: Update TagLib FLAC tests for recent VorbisComments changes --- tests/taglib/test_flac.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/taglib/test_flac.rs b/tests/taglib/test_flac.rs index d792d8cb..70c1157d 100644 --- a/tests/taglib/test_flac.rs +++ b/tests/taglib/test_flac.rs @@ -190,7 +190,6 @@ fn test_remove_all_pictures() { } #[test] -#[ignore] // TODO: `VorbisComments::get` is case-sensitive for now, it shouldn't be. fn test_repeated_save_1() { let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); { @@ -367,8 +366,8 @@ fn test_properties() { file.rewind().unwrap(); f.vorbis_comments().unwrap().save_to(&mut file).unwrap(); } + file.rewind().unwrap(); { - file.rewind().unwrap(); let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert_eq!(f.vorbis_comments(), Some(&tag)); @@ -376,14 +375,17 @@ fn test_properties() { } #[test] -#[ignore] // TODO: Keys are not yet validated fn test_invalid() { let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + // NOTE: In TagLib, there's a `setProperties` method. This is equivalent. + f.vorbis_comments_mut().unwrap().clear(); + f.vorbis_comments_mut() .unwrap() .push(String::from("H\x00c4\x00d6"), String::from("bla")); + assert!(f.vorbis_comments().unwrap().is_empty()); } #[test] From 6137bad7bdc470de0834013b1ec616148f5b2955 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 22 Apr 2023 22:11:35 -0400 Subject: [PATCH 26/75] Tests: Finish TagLib AIFF tests --- tests/taglib/data/duplicate_id3v2.aiff | Bin 0 -> 8124 bytes tests/taglib/test_aiff.rs | 34 +++++++++++++------------ 2 files changed, 18 insertions(+), 16 deletions(-) create mode 100644 tests/taglib/data/duplicate_id3v2.aiff diff --git a/tests/taglib/data/duplicate_id3v2.aiff b/tests/taglib/data/duplicate_id3v2.aiff new file mode 100644 index 0000000000000000000000000000000000000000..6703583f2126062657b1f854e1165d7c84968893 GIT binary patch literal 8124 zcmeI0&2D5x5{0kZ<;E})LM+%auh0!^M9)A=E%7s&_7fmjv;-Symb}p{c{ZT2O>w^X zB5sypBpv{lRIYpTW<;JiaUwFWpML(|Pq*#eAD=vX`t;wQf12y>#n|M3R9iwNNTi^I+4wl^KlfSvIznPcO>+Lf37x_J}r}*Fw zOcz<@&|kdh*VPY=Ni@A?`@fCVZF%Qvd@uqI#}a>|2;y83(adFSxCjsX6Bqz7AJ@Fz z*}>A8u1d^ua~}JA@)zu|HT-P3yQxSAt5#W|XM$#;?zQ9f5L^#~?+_Aib4Au}8xwr8 z(p{XOTlw3cX6E^cuZ3<<-i>#%2>WtzcLbHxx_L_8YF1(~TQkBaalwL`7+%Jk_{SIQ zmHOgoqJc#^@cro4Lgjm_{pPSiC9b zgBYLM%C+8GM^$5>DyBtN>(Eh3eT8MRG6=w-H&&dEtFA_C><7Ql)8da_C%4T{#)z%z zhgJI?54WlHpHuVLia~9_9y%OPT922C2TzU5-SAa>p|xde71*h(n`eIKC0At2`dEF* z49dmCT@_oFDgw(=9sPz$c}9ec8u9l)?=16S%H3U*`kgM?dB}RJ4p`Uc_8l^2os|xPq*2D^ zR9SeypSsJV(u-r(fb5awlbN|&b%bZL?W$mAu4*zGca!Tx$oU11nyX^1YNC8v6;1 zk&AEGG!ML&akl4tCbO*S8mhvx6nhPdz=x*~wL~u%mGe8S$*IwtR%M=-#nQhx9G$(8 z_cLS5)6Bo9eRe8%ItfViJqS>cs-74skC|si$seo~(x4?a)xs&_GOP5;?N_vPsa;d? zRaMrCmB*k!V|g|miKy%ht;Kpqt;iucEU#sC_Y8xkh>TCIRlH74@LXQvX)2hN+&Mjk zFDpG%<{G)EM!$NRo++lm*4Zqc*cDnc_}R0F{fhOJT{%}1qXuwI)~xA?p!g%;%)sYf zSFOcS=~Vl~HHwq5S|>hyZ<|cZ4*RSpD5_L@{w7bcI92MXY2{S4`VKrbj&1FI?ad6x zk|>Q-(WIJA1JN*OiMw$PG!TqXXB z#p)U5iIZr3XIBB_Yk2f()u6gC5|GHQ3d>!iLZ_aZ==JG4nO7+Ol!LOY+>9cKZkSqE zagdMqDtT1{mSXl)hC=NTMA7j#);;p}HC`rScV_=jW3{P;g$N5yy6NQcVMlo*U1d~Z zR#_h$?Bi&)YM?d9U1Hb~nfuX}yL{;=GnnNUtJ5Ae=g-zh&l|nSH&tsLlV9FsF5`m{ z>YtdzQNLM740~5@TbReMk&Wg?1Nq(a$Ns_p{MKHhPJL5qG`I^|73ceqD)7Cm=XNQIY8&Gqh$cziYxYHV&yH3 zlU!%HYEwo*dAIAKiFJ+xJrVlWvCa@!%eXkF!h@y|O+A&Wh^a>pc~5;k{ypz_A|TdQ zPG%LNVrZ1%xrf#05%MbMTIqchQ$3~`R!23`jPE(XQ!#zY^5|#QaYtP55)&0RXhqdg zZLmzWbw%YF%u!ugy)vQ=c0`Nn%%&=X@v6j%udp=}?z8OtUZG{IRnYz)?eaGjYEHJR zkDp)R>e)x{{d3z6ALKVZZ`&^4zj}7{I2W(wd-dYw*Izxldh*G?df>^|Uwr?~qpSaX z{D`UfKKb_Li|<}?cmKI(;GTiMDFZLQ{HreV_>W!W@t?W~UFZID&%iwce^Um22R5}3 AyZ`_I literal 0 HcmV?d00001 diff --git a/tests/taglib/test_aiff.rs b/tests/taglib/test_aiff.rs index 91439fb0..7da42d55 100644 --- a/tests/taglib/test_aiff.rs +++ b/tests/taglib/test_aiff.rs @@ -1,6 +1,7 @@ -use lofty::{Accessor, AudioFile, FileType, TaggedFileExt}; +use lofty::{Accessor, AudioFile, FileType, ParseOptions, TaggedFileExt}; -use std::io::Seek; +use lofty::iff::aiff::AiffFile; +use std::io::{Read, Seek}; use crate::util::get_filetype; use crate::{assert_delta, temp_file}; @@ -74,7 +75,7 @@ fn test_save_id3v2() { let mut tag = tfile.tag(lofty::TagType::Id3v2).unwrap().to_owned(); assert_eq!(tag.title().as_deref(), Some("TitleXXX")); - tag.set_title("".to_string()); + tag.set_title(String::new()); tfile.insert_tag(tag); file.rewind().unwrap(); tfile.save_to(&mut file).unwrap(); @@ -92,8 +93,15 @@ fn test_save_id3v2() { } } -// TODO: testSaveID3v23 -// TODO: testDuplicateID3v2 +#[test] +#[ignore] // TODO: Support writing ID3v2.3 tags +fn test_save_id3v23() {} + +#[test] +#[ignore] +fn test_duplicate_id3v2() { + // Marker test, Lofty will overwrite values in the original tag with any new values it finds in the next tag. +} #[test] #[ignore] @@ -104,14 +112,8 @@ fn test_fuzzed_file1() { ); } -// the file doesn't even have a valid signature -// #[test] -// #[ignore] -// fn test_fuzzed_file2() { -// let mut file = File::open("tests/taglib/data/excessive_alloc.aif").unwrap(); -// -// let mut buf = [0; 12]; -// file.read_exact(&mut buf).unwrap(); -// -// assert_eq!(FileType::from_buffer(&buf).unwrap(), FileType::AIFF); -// } +#[test] +#[ignore] +fn test_fuzzed_file2() { + // Marker test, this file doesn't even have a valid signature. No idea how TagLib manages to read it. +} From b7a49b459dcafacdc3548846c6af7a9fda3740a9 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Wed, 5 Jul 2023 19:41:15 -0400 Subject: [PATCH 27/75] Tests: Update TagLib tests for recent ID3v2 changes --- tests/taglib/test_ape.rs | 6 +- tests/taglib/test_flac.rs | 4 +- tests/taglib/test_id3v1.rs | 4 +- tests/taglib/test_id3v2.rs | 125 ++++++++++++++++++----------------- tests/taglib/test_mpeg.rs | 18 ++--- tests/taglib/test_wav.rs | 10 +-- tests/taglib/test_wavpack.rs | 6 +- 7 files changed, 88 insertions(+), 85 deletions(-) diff --git a/tests/taglib/test_ape.rs b/tests/taglib/test_ape.rs index b7cc0ad0..40d4f927 100644 --- a/tests/taglib/test_ape.rs +++ b/tests/taglib/test_ape.rs @@ -6,7 +6,7 @@ use std::io::Seek; use std::time::Duration; use lofty::ape::{ApeFile, ApeItem, ApeTag}; -use lofty::id3::v1::ID3v1Tag; +use lofty::id3::v1::Id3v1Tag; use lofty::{Accessor, AudioFile, FileType, ItemValue, ParseOptions, TagExt}; fn test_399(path: &str) { @@ -99,7 +99,7 @@ fn test_strip_and_properties() { ape_tag.set_title(String::from("APE")); ape_file.set_ape(ape_tag); - let mut id3v1_tag = ID3v1Tag::default(); + let mut id3v1_tag = Id3v1Tag::default(); id3v1_tag.set_title(String::from("ID3v1")); ape_file.set_id3v1(id3v1_tag); @@ -363,7 +363,7 @@ fn test_repeated_save() { ape_file.save_to(&mut file).unwrap(); file.rewind().unwrap(); - let mut id3v1_tag = ID3v1Tag::default(); + let mut id3v1_tag = Id3v1Tag::default(); id3v1_tag.set_title(String::from("01234 56789 ABCDE FGHIJ")); ape_file.set_id3v1(id3v1_tag); ape_file.ape_mut().unwrap().set_title(String::from( diff --git a/tests/taglib/test_flac.rs b/tests/taglib/test_flac.rs index 70c1157d..4e86812a 100644 --- a/tests/taglib/test_flac.rs +++ b/tests/taglib/test_flac.rs @@ -3,7 +3,7 @@ use crate::temp_file; use std::io::{Read, Seek, SeekFrom}; use lofty::flac::FlacFile; -use lofty::id3::v2::ID3v2Tag; +use lofty::id3::v2::Id3v2Tag; use lofty::ogg::{OggPictureStorage, VorbisComments}; use lofty::{ Accessor, AudioFile, MimeType, ParseOptions, Picture, PictureInformation, PictureType, TagExt, @@ -490,7 +490,7 @@ fn test_empty_id3v2() { let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); - f.set_id3v2(ID3v2Tag::default()); + f.set_id3v2(Id3v2Tag::default()); f.save_to(&mut file).unwrap(); } file.rewind().unwrap(); diff --git a/tests/taglib/test_id3v1.rs b/tests/taglib/test_id3v1.rs index 61f428a6..948c1cc5 100644 --- a/tests/taglib/test_id3v1.rs +++ b/tests/taglib/test_id3v1.rs @@ -2,7 +2,7 @@ use crate::temp_file; use std::io::Seek; -use lofty::id3::v1::{ID3v1Tag, GENRES}; +use lofty::id3::v1::{Id3v1Tag, GENRES}; use lofty::mpeg::MpegFile; use lofty::{Accessor, AudioFile, ParseOptions}; @@ -14,7 +14,7 @@ fn test_strip_whitespace() { let mut f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); - let mut tag = ID3v1Tag::default(); + let mut tag = Id3v1Tag::default(); tag.set_artist(String::from("Artist ")); f.set_id3v1(tag); f.save_to(&mut file).unwrap(); diff --git a/tests/taglib/test_id3v2.rs b/tests/taglib/test_id3v2.rs index e183348f..4637e811 100644 --- a/tests/taglib/test_id3v2.rs +++ b/tests/taglib/test_id3v2.rs @@ -1,13 +1,14 @@ use crate::temp_file; use lofty::id3::v2::{ AttachedPictureFrame, CommentFrame, ExtendedTextFrame, ExtendedUrlFrame, Frame, FrameFlags, - FrameId, FrameValue, GEOBInformation, GeneralEncapsulatedObject, ID3v2Tag, ID3v2Version, - Popularimeter, SyncTextContentType, SyncTextInformation, SynchronizedText, TimestampFormat, - UniqueFileIdentifierFrame, UrlLinkFrame, + FrameId, FrameValue, GeneralEncapsulatedObject, Id3v2Tag, Id3v2Version, Popularimeter, + SyncTextContentType, SynchronizedText, TimestampFormat, UniqueFileIdentifierFrame, + UrlLinkFrame, }; use lofty::mpeg::MpegFile; use lofty::{ - Accessor, AudioFile, MimeType, ParseOptions, Picture, PictureType, TagExt, TextEncoding, + Accessor, AudioFile, MimeType, ParseOptions, ParsingMode, Picture, PictureType, TagExt, + TextEncoding, }; use std::borrow::Cow; use std::io::Seek; @@ -60,13 +61,13 @@ fn test_read_string_field() { #[test] fn test_parse_apic() { let f = AttachedPictureFrame::parse( - b"\ + &mut &b"\ \x00\ m\x00\ \x01\ d\x00\ - \x00", - ID3v2Version::V4, + \x00"[..], + Id3v2Version::V4, ) .unwrap(); assert_eq!(f.picture.mime_type(), &MimeType::Unknown(String::from("m"))); @@ -77,11 +78,11 @@ fn test_parse_apic() { #[test] fn test_parse_apic_utf16_bom() { let f = AttachedPictureFrame::parse( - b"\ + &mut &b"\ \x01\x69\x6d\x61\x67\x65\ \x2f\x6a\x70\x65\x67\x00\x00\xfe\xff\x00\x63\x00\x6f\x00\x76\x00\ - \x65\x00\x72\x00\x2e\x00\x6a\x00\x70\x00\x67\x00\x00\xff\xd8\xff", - ID3v2Version::V4, + \x65\x00\x72\x00\x2e\x00\x6a\x00\x70\x00\x67\x00\x00\xff\xd8\xff"[..], + Id3v2Version::V4, ) .unwrap(); @@ -94,13 +95,13 @@ fn test_parse_apic_utf16_bom() { #[test] fn test_parse_apicv22() { let frame = AttachedPictureFrame::parse( - b"\ + &mut &b"\ \x00\ JPG\ \x01\ d\x00\ - \x00", - ID3v2Version::V2, + \x00"[..], + Id3v2Version::V2, ) .unwrap(); @@ -122,7 +123,7 @@ fn test_render_apic() { }; assert_eq!( - f.as_bytes(ID3v2Version::V4).unwrap(), + f.as_bytes(Id3v2Version::V4).unwrap(), b"\ \x03\ image/png\x00\ @@ -149,20 +150,18 @@ fn test_parse_geob() { \x00", ) .unwrap(); - assert_eq!(f.information.mime_type.as_deref(), Some("m")); - assert_eq!(f.information.file_name.as_deref(), Some("f")); - assert_eq!(f.information.descriptor.as_deref(), Some("d")); + assert_eq!(f.mime_type.as_deref(), Some("m")); + assert_eq!(f.file_name.as_deref(), Some("f")); + assert_eq!(f.descriptor.as_deref(), Some("d")); } #[test] fn test_render_geob() { let f = GeneralEncapsulatedObject { - information: GEOBInformation { - encoding: TextEncoding::Latin1, - mime_type: Some(String::from("application/octet-stream")), - file_name: Some(String::from("test.bin")), - descriptor: Some(String::from("Description")), - }, + encoding: TextEncoding::Latin1, + mime_type: Some(String::from("application/octet-stream")), + file_name: Some(String::from("test.bin")), + descriptor: Some(String::from("Description")), data: vec![0x01; 3], }; @@ -180,10 +179,10 @@ fn test_render_geob() { #[test] fn test_parse_popm() { let f = Popularimeter::parse( - b"\ + &mut &b"\ email@example.com\x00\ \x02\ - \x00\x00\x00\x03", + \x00\x00\x00\x03"[..], ) .unwrap(); assert_eq!(f.email, "email@example.com"); @@ -194,9 +193,9 @@ fn test_parse_popm() { #[test] fn test_parse_popm_without_counter() { let f = Popularimeter::parse( - b"\ + &mut &b"\ email@example.com\x00\ - \x02", + \x02"[..], ) .unwrap(); assert_eq!(f.email, "email@example.com"); @@ -241,7 +240,7 @@ fn test_popm_from_file() { let mut foo = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); - let mut tag = ID3v2Tag::new(); + let mut tag = Id3v2Tag::new(); tag.insert( Frame::new("POPM", FrameValue::Popularimeter(f), FrameFlags::default()).unwrap(), ); @@ -276,6 +275,7 @@ fn test_parse_unique_file_identifier_frame() { &mut &b"\ owner\x00\ \x00\x01\x02"[..], + ParsingMode::Strict, ) .unwrap() .unwrap(); @@ -290,6 +290,7 @@ fn test_parse_empty_unique_file_identifier_frame() { &mut &b"\ \x00\ "[..], + ParsingMode::Strict, ); // NOTE: TagLib considers a missing owner to be valid, we do not @@ -313,24 +314,28 @@ owner\x00\ #[test] fn test_parse_url_link_frame() { - let f = UrlLinkFrame::parse(b"http://example.com").unwrap().unwrap(); + let f = UrlLinkFrame::parse(&mut &b"http://example.com"[..]) + .unwrap() + .unwrap(); assert_eq!(f.url(), "http://example.com"); } #[test] fn test_render_url_link_frame() { - let f = UrlLinkFrame::parse(b"http://example.com").unwrap().unwrap(); + let f = UrlLinkFrame::parse(&mut &b"http://example.com"[..]) + .unwrap() + .unwrap(); assert_eq!(f.as_bytes(), b"http://example.com"); } #[test] fn test_parse_user_url_link_frame() { let f = ExtendedUrlFrame::parse( - b"\ + &mut &b"\ \x00\ foo\x00\ - http://example.com", - ID3v2Version::V4, + http://example.com"[..], + Id3v2Version::V4, ) .unwrap() .unwrap(); @@ -380,11 +385,11 @@ Lyrics\x00\ ) .unwrap(); - assert_eq!(f.information.encoding, TextEncoding::Latin1); - assert_eq!(f.information.language, *b"eng"); - assert_eq!(f.information.timestamp_format, TimestampFormat::MS); - assert_eq!(f.information.content_type, SyncTextContentType::Lyrics); - assert_eq!(f.information.description.as_deref(), Some("foo")); + assert_eq!(f.encoding, TextEncoding::Latin1); + assert_eq!(f.language, *b"eng"); + assert_eq!(f.timestamp_format, TimestampFormat::MS); + assert_eq!(f.content_type, SyncTextContentType::Lyrics); + assert_eq!(f.description.as_deref(), Some("foo")); assert_eq!(f.content.len(), 2); assert_eq!(f.content[0].1, "Example"); @@ -409,11 +414,11 @@ fn test_parse_synchronized_lyrics_frame_with_empty_description() { ) .unwrap(); - assert_eq!(f.information.encoding, TextEncoding::Latin1); - assert_eq!(f.information.language, *b"eng"); - assert_eq!(f.information.timestamp_format, TimestampFormat::MS); - assert_eq!(f.information.content_type, SyncTextContentType::Lyrics); - assert!(f.information.description.is_none()); + assert_eq!(f.encoding, TextEncoding::Latin1); + assert_eq!(f.language, *b"eng"); + assert_eq!(f.timestamp_format, TimestampFormat::MS); + assert_eq!(f.content_type, SyncTextContentType::Lyrics); + assert!(f.description.is_none()); assert_eq!(f.content.len(), 2); assert_eq!(f.content[0].1, "Example"); @@ -425,13 +430,11 @@ fn test_parse_synchronized_lyrics_frame_with_empty_description() { #[test] fn test_render_synchronized_lyrics_frame() { let f = SynchronizedText { - information: SyncTextInformation { - encoding: TextEncoding::Latin1, - language: *b"eng", - timestamp_format: TimestampFormat::MS, - content_type: SyncTextContentType::Lyrics, - description: Some(String::from("foo")), - }, + encoding: TextEncoding::Latin1, + language: *b"eng", + timestamp_format: TimestampFormat::MS, + content_type: SyncTextContentType::Lyrics, + description: Some(String::from("foo")), content: vec![ (1234, String::from("Example")), (4567, String::from("Lyrics")), @@ -464,11 +467,11 @@ fn test_render_event_timing_codes_frame() {} #[test] fn test_parse_comments_frame() { let f = CommentFrame::parse( - b"\x03\ + &mut &b"\x03\ deu\ Description\x00\ - Text", - ID3v2Version::V4, + Text"[..], + Id3v2Version::V4, ) .unwrap() .unwrap(); @@ -521,11 +524,11 @@ fn test_render_private_frame() {} #[test] fn test_parse_user_text_identification_frame() { let frame_without_description = ExtendedUrlFrame::parse( - b"\ + &mut &b"\ \x00\ \x00\ - Text", - ID3v2Version::V4, + Text"[..], + Id3v2Version::V4, ) .unwrap() .unwrap(); @@ -534,11 +537,11 @@ fn test_parse_user_text_identification_frame() { assert_eq!(frame_without_description.content, String::from("Text")); let frame_with_description = ExtendedUrlFrame::parse( - b"\ + &mut &b"\ \x00\ Description\x00\ - Text", - ID3v2Version::V4, + Text"[..], + Id3v2Version::V4, ) .unwrap() .unwrap(); @@ -601,7 +604,7 @@ fn test_save_utf16_comment() { let mut foo = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); - let mut tag = ID3v2Tag::new(); + let mut tag = Id3v2Tag::new(); tag.insert( Frame::new( "COMM", @@ -761,7 +764,7 @@ fn test_save_and_strip_id3v1_should_not_add_frame_from_id3v1_to_id3v2() { let mut foo = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); - let mut tag = ID3v2Tag::new(); + let mut tag = Id3v2Tag::new(); tag.set_artist(String::from("Artist")); foo.set_id3v2(tag); foo.save_to(&mut file).unwrap(); diff --git a/tests/taglib/test_mpeg.rs b/tests/taglib/test_mpeg.rs index afeb1a57..7724a335 100644 --- a/tests/taglib/test_mpeg.rs +++ b/tests/taglib/test_mpeg.rs @@ -4,8 +4,8 @@ use std::fs::File; use std::io::Seek; use lofty::ape::ApeTag; -use lofty::id3::v1::ID3v1Tag; -use lofty::id3::v2::{ID3v2Tag, ID3v2Version}; +use lofty::id3::v1::Id3v1Tag; +use lofty::id3::v2::{Id3v2Tag, Id3v2Version}; use lofty::mpeg::MpegFile; use lofty::{Accessor, AudioFile, ParseOptions}; @@ -127,7 +127,7 @@ fn test_save_id3v24() { file.rewind().unwrap(); assert!(f.id3v2().is_none()); - let mut tag = ID3v2Tag::default(); + let mut tag = Id3v2Tag::default(); tag.set_title(xxx.clone()); tag.set_artist(String::from("Artist A")); f.set_id3v2(tag); @@ -136,7 +136,7 @@ fn test_save_id3v24() { file.rewind().unwrap(); { let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); - assert_eq!(f.id3v2().unwrap().original_version(), ID3v2Version::V4); + assert_eq!(f.id3v2().unwrap().original_version(), Id3v2Version::V4); assert_eq!(f.id3v2().unwrap().artist().as_deref(), Some("Artist A")); assert_eq!(f.id3v2().unwrap().title().as_deref(), Some(xxx.as_str())); } @@ -159,7 +159,7 @@ fn test_save_id3v23() { file.rewind().unwrap(); assert!(f.id3v2().is_none()); - let mut tag = ID3v2Tag::default(); + let mut tag = Id3v2Tag::default(); tag.set_title(xxx.clone()); tag.set_artist(String::from("Artist A")); f.set_id3v2(tag); @@ -168,7 +168,7 @@ fn test_save_id3v23() { file.rewind().unwrap(); { let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); - assert_eq!(f.id3v2().unwrap().original_version(), ID3v2Version::V3); + assert_eq!(f.id3v2().unwrap().original_version(), Id3v2Version::V3); assert_eq!(f.id3v2().unwrap().artist().as_deref(), Some("Artist A")); assert_eq!(f.id3v2().unwrap().title().as_deref(), Some(xxx.as_str())); } @@ -201,13 +201,13 @@ fn test_strip_and_properties() { let mut f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); - let mut id3v2 = ID3v2Tag::default(); + let mut id3v2 = Id3v2Tag::default(); id3v2.set_title(String::from("ID3v2")); f.set_id3v2(id3v2); let mut ape = ApeTag::default(); ape.set_title(String::from("APE")); f.set_ape(ape); - let mut id3v1 = ID3v1Tag::default(); + let mut id3v1 = Id3v1Tag::default(); id3v1.set_title(String::from("ID3v1")); f.set_id3v1(id3v1); f.save_to(&mut file).unwrap(); @@ -262,7 +262,7 @@ fn test_repeated_save_3() { f.save_to(&mut file).unwrap(); } { - let mut id3v1 = ID3v1Tag::default(); + let mut id3v1 = Id3v1Tag::default(); id3v1.set_title(String::from("01234 56789 ABCDE FGHIJ")); f.set_id3v1(id3v1); } diff --git a/tests/taglib/test_wav.rs b/tests/taglib/test_wav.rs index e0f4f44c..de609503 100644 --- a/tests/taglib/test_wav.rs +++ b/tests/taglib/test_wav.rs @@ -1,5 +1,5 @@ use crate::temp_file; -use lofty::id3::v2::{ID3v2Tag, ID3v2Version}; +use lofty::id3::v2::{Id3v2Tag, Id3v2Version}; use lofty::iff::wav::{RIFFInfoList, WavFile, WavFormat}; use lofty::{Accessor, AudioFile, ParseOptions, TagType}; use std::io::{Cursor, Read, Seek, SeekFrom, Write}; @@ -80,7 +80,7 @@ fn test_id3v2_tag() { file.rewind().unwrap(); assert!(f.id3v2().is_none()); - let mut id3v2 = ID3v2Tag::default(); + let mut id3v2 = Id3v2Tag::default(); id3v2.set_title(String::from("Title")); id3v2.set_artist(String::from("Artist")); f.set_id3v2(id3v2); @@ -126,7 +126,7 @@ fn test_save_id3v23() { { let f2 = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); let tag = f2.id3v2().unwrap(); - assert_eq!(tag.original_version(), ID3v2Version::V3); + assert_eq!(tag.original_version(), Id3v2Version::V3); assert_eq!(tag.artist().as_deref(), Some("Artist A")); assert_eq!(tag.title().as_deref(), Some(&*xxx)); } @@ -176,7 +176,7 @@ fn test_strip_tags() { let mut f = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); - let mut id3v2 = ID3v2Tag::default(); + let mut id3v2 = Id3v2Tag::default(); id3v2.set_title(String::from("test title")); f.set_id3v2(id3v2); @@ -299,7 +299,7 @@ fn test_file_with_garbage_appended() { let mut f = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); - let mut id3v2 = ID3v2Tag::default(); + let mut id3v2 = Id3v2Tag::default(); id3v2.set_title(String::from("ID3v2 Title")); f.set_id3v2(id3v2); diff --git a/tests/taglib/test_wavpack.rs b/tests/taglib/test_wavpack.rs index b4fe13a9..b2f931c2 100644 --- a/tests/taglib/test_wavpack.rs +++ b/tests/taglib/test_wavpack.rs @@ -3,7 +3,7 @@ use crate::temp_file; use std::io::Seek; use lofty::ape::ApeTag; -use lofty::id3::v1::ID3v1Tag; +use lofty::id3::v1::Id3v1Tag; use lofty::wavpack::WavPackFile; use lofty::{Accessor, AudioFile, ParseOptions}; @@ -100,7 +100,7 @@ fn test_strip_and_properties() { ape.set_title(String::from("APE")); f.set_ape(ape); - let mut id3v1 = ID3v1Tag::default(); + let mut id3v1 = Id3v1Tag::default(); id3v1.set_title(String::from("ID3v1")); f.set_id3v1(id3v1); @@ -139,7 +139,7 @@ fn test_repeated_save() { f.save_to(&mut file).unwrap(); file.rewind().unwrap(); - let mut id3v1 = ID3v1Tag::default(); + let mut id3v1 = Id3v1Tag::default(); id3v1.set_title(String::from("01234 56789 ABCDE FGHIJ")); f.set_id3v1(id3v1); f.ape_mut().unwrap().set_title(String::from( From 6f751fb7e628dc5cbcb1648d8a6bb0450709c9b0 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Wed, 5 Jul 2023 19:42:16 -0400 Subject: [PATCH 28/75] Tests: Add TagLib Musepack fileref test --- tests/taglib/test_fileref.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/taglib/test_fileref.rs b/tests/taglib/test_fileref.rs index 1bc62d3c..c2e2e89d 100644 --- a/tests/taglib/test_fileref.rs +++ b/tests/taglib/test_fileref.rs @@ -80,8 +80,7 @@ fn file_ref_save(path: &str, expected_file_type: FileType) { #[test] #[ignore] fn test_musepack() { - // TODO: We don't support MusePack yet - // file_ref_save("click.mpc", FileType::MusePack); + file_ref_save("click.mpc", FileType::Mpc); } #[test] From 648efe364d3870fe37c888db1569dba1768d9240 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Wed, 5 Jul 2023 20:16:40 -0400 Subject: [PATCH 29/75] Tests: Add TagLib Musepack tests --- tests/taglib/data/infloop.mpc | Bin 0 -> 434 bytes tests/taglib/data/segfault.mpc | Bin 0 -> 19 bytes tests/taglib/data/segfault2.mpc | 1 + tests/taglib/data/sv4_header.mpc | Bin 0 -> 128 bytes tests/taglib/data/sv5_header.mpc | Bin 0 -> 128 bytes tests/taglib/data/sv8_header.mpc | Bin 0 -> 114 bytes tests/taglib/data/zerodiv.mpc | Bin 0 -> 405 bytes tests/taglib/main.rs | 1 + tests/taglib/test_mpc.rs | 193 +++++++++++++++++++++++++++++++ tests/taglib/util/mod.rs | 8 ++ 10 files changed, 203 insertions(+) create mode 100644 tests/taglib/data/infloop.mpc create mode 100644 tests/taglib/data/segfault.mpc create mode 100644 tests/taglib/data/segfault2.mpc create mode 100644 tests/taglib/data/sv4_header.mpc create mode 100644 tests/taglib/data/sv5_header.mpc create mode 100644 tests/taglib/data/sv8_header.mpc create mode 100644 tests/taglib/data/zerodiv.mpc create mode 100644 tests/taglib/test_mpc.rs diff --git a/tests/taglib/data/infloop.mpc b/tests/taglib/data/infloop.mpc new file mode 100644 index 0000000000000000000000000000000000000000..46861ab378cc3060fb759333490bb4d2852ccd36 GIT binary patch literal 434 zcmeYbaP|&9fgfCgN*5#-zhGv70Qc0qqErT_#GIVO6fuxcNKs;OHUooWQFdl=395O- z=_{dukwB*~WR(_|D5T{VDU_rZm#|@Rz9v*GB(o$Zl_3|+wT3KE>B!W?A_gM^0|UnZ j*APc{*N6)Z;D5wsil6)pQDvY{}wzBH4T&wTPvrFTA(>cv{&D-_Dah=cBT5|n@ iE3UO<#~#&>*ohqgTB!C%gDl*OOm-ISqH9VS7Q>v%RY5rb literal 0 HcmV?d00001 diff --git a/tests/taglib/data/sv5_header.mpc b/tests/taglib/data/sv5_header.mpc new file mode 100644 index 0000000000000000000000000000000000000000..6d17e65f0f865303397cdb08632cca5a2aa0365a GIT binary patch literal 128 zcmV-`0Du3%FCYN&0{{RVcBcU78pg2y2S5XsQc5XpYAK~=`TMqG7PBld%R&|wvn-27 z)(~SG+b-oV^~l!Gc|2h@R~)T+MB#OHHdkBzU)gt`zIpg}+1_WT^O~=66~40@oh*DH|+sQguls;y*_RL literal 0 HcmV?d00001 diff --git a/tests/taglib/data/sv8_header.mpc b/tests/taglib/data/sv8_header.mpc new file mode 100644 index 0000000000000000000000000000000000000000..3405545a29460883251a5ccd6135ae440bca5815 GIT binary patch literal 114 zcmeYbaP|)N;Crv7*~rn-9LK;a8RX8x=vo_f28=z~0~zHQg8eyM7=XYrfJKH(ED#*R L#wc_k*p(Ro^T!wZ literal 0 HcmV?d00001 diff --git a/tests/taglib/data/zerodiv.mpc b/tests/taglib/data/zerodiv.mpc new file mode 100644 index 0000000000000000000000000000000000000000..d3ea57c75ca8a58f8bcc9bf0388935d59f8d70d5 GIT binary patch literal 405 zcmeYbaP|)N;Gg>Ipa@4}XS)kS0|OX1<|LKoGB_oAYnU*r0Omshf*Zff{mv8pNPtA(!>Pz6wf z0!T@+LUF5wf&$P~h4RdjjQ>DT4AJ|VAp})#WNKm&gS~-)fn$Jch@-n}`~`LfhLsE) z3~X^FD3+ip)Jy}q&kSN6Lx=zalI;x9V8`SZpgQCK|B}RXJkI!o("tests/taglib/data/sv8_header.mpc"); + + let MpcProperties::Sv8(properties) = f.properties() else { + panic!("Got the wrong properties somehow") + }; + + assert_eq!(properties.version(), 8); + assert_eq!(properties.duration().as_secs(), 1); + assert_eq!(properties.duration().as_millis(), 1497); + assert_eq!(properties.audio_bitrate(), 1); + assert_eq!(properties.channels(), 2); + assert_eq!(properties.sample_rate(), 44100); + // TODO + // assert_eq!(properties.sample_frames(), 66014); +} + +#[test] +fn test_properties_sv7() { + let f = get_file::("tests/taglib/data/click.mpc"); + + let MpcProperties::Sv7(properties) = f.properties() else { + panic!("Got the wrong properties somehow") + }; + + assert_eq!(properties.duration().as_secs(), 0); + assert_eq!(properties.duration().as_millis(), 40); + assert_eq!(properties.audio_bitrate(), 318); + assert_eq!(properties.channels(), 2); + assert_eq!(properties.sample_rate(), 44100); + // TODO + // assert_eq!(properties.sample_frames(), 1760); + + assert_eq!(properties.title_gain(), 14221); + assert_eq!(properties.title_peak(), 19848); + assert_eq!(properties.album_gain(), 14221); + assert_eq!(properties.album_peak(), 19848); +} + +#[test] +fn test_properties_sv5() { + let f = get_file::("tests/taglib/data/sv5_header.mpc"); + + let MpcProperties::Sv4to6(properties) = f.properties() else { + panic!("Got the wrong properties somehow") + }; + + assert_eq!(properties.stream_version(), 5); + assert_eq!(properties.duration().as_secs(), 26); + assert_eq!(properties.duration().as_millis(), 26371); + assert_eq!(properties.audio_bitrate(), 0); + assert_eq!(properties.channels(), 2); + assert_eq!(properties.sample_rate(), 44100); + // TODO + // assert_eq!(properties.sample_frames(), 1162944); +} + +#[test] +fn test_properties_sv4() { + let f = get_file::("tests/taglib/data/sv4_header.mpc"); + + let MpcProperties::Sv4to6(properties) = f.properties() else { + panic!("Got the wrong properties somehow") + }; + + assert_eq!(properties.stream_version(), 4); + assert_eq!(properties.duration().as_secs(), 26); + assert_eq!(properties.duration().as_millis(), 26371); + assert_eq!(properties.audio_bitrate(), 0); + assert_eq!(properties.channels(), 2); + assert_eq!(properties.sample_rate(), 44100); + // TODO + // assert_eq!(properties.sample_frames(), 1162944); +} + +#[test] +fn test_fuzzed_file1() { + let _ = Probe::open("tests/taglib/data/zerodiv.mpc") + .unwrap() + .guess_file_type() + .unwrap(); +} + +#[test] +fn test_fuzzed_file2() { + let _ = Probe::open("tests/taglib/data/infloop.mpc") + .unwrap() + .guess_file_type() + .unwrap(); +} + +#[test] +fn test_fuzzed_file3() { + let _ = Probe::open("tests/taglib/data/segfault.mpc") + .unwrap() + .guess_file_type() + .unwrap(); +} + +#[test] +fn test_fuzzed_file4() { + let _ = Probe::open("tests/taglib/data/segfault2.mpc") + .unwrap() + .guess_file_type() + .unwrap(); +} + +#[test] +fn test_strip_and_properties() { + let mut file = temp_file!("tests/taglib/data/click.mpc"); + + { + let mut f = MpcFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut ape = ApeTag::new(); + ape.set_title(String::from("APE")); + f.set_ape(ape); + + let mut id3v1 = Id3v1Tag::new(); + id3v1.set_title(String::from("ID3v1")); + f.set_id3v1(id3v1); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let mut f = MpcFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert_eq!(f.ape().unwrap().title().as_deref(), Some("APE")); + f.ape_mut().unwrap().clear(); + assert_eq!(f.id3v1().unwrap().title().as_deref(), Some("ID3v1")); + f.id3v1_mut().unwrap().clear(); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = MpcFile::read_from(&mut file, ParseOptions::new()).unwrap(); + + assert!(f.ape().is_none()); + assert!(f.id3v1().is_none()); + } +} + +#[test] +fn test_repeated_save() { + let mut file = temp_file!("tests/taglib/data/click.mpc"); + + { + let mut f = MpcFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + assert!(f.ape().is_none()); + assert!(f.id3v1().is_none()); + + let mut ape = ApeTag::new(); + ape.set_title(String::from("01234 56789 ABCDE FGHIJ")); + f.set_ape(ape); + + f.save_to(&mut file).unwrap(); + file.rewind().unwrap(); + + f.ape_mut().unwrap().set_title(String::from("0")); + + f.save_to(&mut file).unwrap(); + file.rewind().unwrap(); + + let mut id3v1 = Id3v1Tag::new(); + id3v1.set_title(String::from("01234 56789 ABCDE FGHIJ")); + f.set_id3v1(id3v1); + f.ape_mut().unwrap().set_title(String::from( + "01234 56789 ABCDE FGHIJ 01234 56789 ABCDE FGHIJ 01234 56789", + )); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = MpcFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(f.ape().is_some()); + assert!(f.id3v1().is_some()); + } +} diff --git a/tests/taglib/util/mod.rs b/tests/taglib/util/mod.rs index f81c89ca..07f8bb5a 100644 --- a/tests/taglib/util/mod.rs +++ b/tests/taglib/util/mod.rs @@ -1,3 +1,6 @@ +use lofty::{AudioFile, ParseOptions}; +use std::fs::File; + /// This function tries to simulate TagLibs isValid function // https://github.com/Serial-ATA/lofty-rs/pull/51#discussion_r873171570 pub fn get_filetype>(path: P) -> lofty::FileType { @@ -7,6 +10,11 @@ pub fn get_filetype>(path: P) -> lofty::FileType { lofty::FileType::from_buffer(&buf).unwrap() } +pub fn get_file(path: &str) -> F { + let mut file = File::open(path).unwrap(); + F::read_from(&mut file, ParseOptions::new()).unwrap() +} + #[macro_export] macro_rules! assert_delta { ($x:expr, $y:expr, $d:expr) => { From f3f394157b93b2d13539e1943ef982e0786553ad Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 8 Jul 2023 23:28:14 -0400 Subject: [PATCH 30/75] Tests: Add TagLib SynchData tests --- tests/taglib/main.rs | 1 + tests/taglib/test_synchdata.rs | 90 ++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 tests/taglib/test_synchdata.rs diff --git a/tests/taglib/main.rs b/tests/taglib/main.rs index 20a8fc19..145e66ac 100644 --- a/tests/taglib/main.rs +++ b/tests/taglib/main.rs @@ -16,6 +16,7 @@ mod test_ogaflac; mod test_ogg; mod test_opus; mod test_speex; +mod test_synchdata; mod test_wav; mod test_wavpack; mod test_xiphcomment; diff --git a/tests/taglib/test_synchdata.rs b/tests/taglib/test_synchdata.rs new file mode 100644 index 00000000..abd3863a --- /dev/null +++ b/tests/taglib/test_synchdata.rs @@ -0,0 +1,90 @@ +use lofty::id3::v2::util::synchsafe::{SynchsafeInteger, UnsynchronizedStream}; +use std::io::Read; + +#[test] +fn test1() { + let v = u32::from_be_bytes([0, 0, 0, 127]); + + assert_eq!(v.unsynch(), 127); + assert_eq!(127u32.synch().unwrap(), v); +} + +#[test] +fn test2() { + let v = u32::from_be_bytes([0, 0, 1, 0]); + + assert_eq!(v.unsynch(), 128); + assert_eq!(128u32.synch().unwrap(), v); +} + +#[test] +fn test3() { + let v = u32::from_be_bytes([0, 0, 1, 1]); + + assert_eq!(v.unsynch(), 129); + assert_eq!(129u32.synch().unwrap(), v); +} + +#[test] +#[ignore] +fn test_to_uint_broken() { + // Marker test, this behavior is not replicated in Lofty +} + +#[test] +#[ignore] +fn test_to_uint_broken_and_too_large() { + // Marker test, this behavior is not replicated in Lofty +} + +#[test] +fn test_decode1() { + let a = [0xFFu8, 0x00u8, 0x00u8]; + + let mut a2 = Vec::new(); + UnsynchronizedStream::new(&mut &a[..]) + .read_to_end(&mut a2) + .unwrap(); + + assert_eq!(a2.len(), 2); + assert_eq!(a2, &[0xFF, 0x00]); +} + +#[test] +fn test_decode2() { + let a = [0xFFu8, 0x44u8]; + + let mut a2 = Vec::new(); + UnsynchronizedStream::new(&mut &a[..]) + .read_to_end(&mut a2) + .unwrap(); + + assert_eq!(a2.len(), 2); + assert_eq!(a2, &[0xFF, 0x44]); +} + +#[test] +fn test_decode3() { + let a = [0xFFu8, 0xFFu8, 0x00u8]; + + let mut a2 = Vec::new(); + UnsynchronizedStream::new(&mut &a[..]) + .read_to_end(&mut a2) + .unwrap(); + + assert_eq!(a2.len(), 2); + assert_eq!(a2, &[0xFFu8, 0xFFu8]); +} + +#[test] +fn test_decode4() { + let a = [0xFFu8, 0xFFu8, 0xFFu8]; + + let mut a2 = Vec::new(); + UnsynchronizedStream::new(&mut &a[..]) + .read_to_end(&mut a2) + .unwrap(); + + assert_eq!(a2.len(), 3); + assert_eq!(a2, &[0xFFu8, 0xFFu8, 0xFFu8]); +} From 14dfefb564f264295a59d737933a8efd1db6f7f3 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 8 Jul 2023 23:44:55 -0400 Subject: [PATCH 31/75] Tests: Fix paths in TagLib tests --- tests/taglib/data/excessive_alloc.mp3 | Bin 0 -> 925 bytes tests/taglib/data/sinewave.flac | Bin 0 -> 64567 bytes tests/taglib/data/zero-length-mdat.m4a | Bin 0 -> 4517 bytes tests/taglib/test_flac.rs | 10 ++++------ tests/taglib/test_mp4.rs | 4 ++-- tests/taglib/test_mpeg.rs | 2 +- 6 files changed, 7 insertions(+), 9 deletions(-) create mode 100644 tests/taglib/data/excessive_alloc.mp3 create mode 100644 tests/taglib/data/sinewave.flac create mode 100644 tests/taglib/data/zero-length-mdat.m4a diff --git a/tests/taglib/data/excessive_alloc.mp3 b/tests/taglib/data/excessive_alloc.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..cd8aa2aba10fa3391f1820ab1d9d4ece730fe888 GIT binary patch literal 925 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 HgBSiA+4#i{ literal 0 HcmV?d00001 diff --git a/tests/taglib/test_flac.rs b/tests/taglib/test_flac.rs index 4e86812a..86703b8d 100644 --- a/tests/taglib/test_flac.rs +++ b/tests/taglib/test_flac.rs @@ -390,15 +390,13 @@ fn test_invalid() { #[test] fn test_audio_properties() { - let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); + let mut file = temp_file!("tests/taglib/data/sinewave.flac"); let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); let properties = f.properties(); assert_eq!(properties.duration().as_secs(), 3); - // NOTE: This is the number reported by `ffprobe`, TagLib is off here. - assert_eq!(properties.duration().as_millis(), 3684); - // TODO: We report 101, ffprobe reports 110, and TagLib reports 145 - // assert_eq!(properties.audio_bitrate(), 145); + assert_eq!(properties.duration().as_millis(), 3550); + assert_eq!(properties.audio_bitrate(), 145); assert_eq!(properties.sample_rate(), 44100); assert_eq!(properties.channels(), 2); assert_eq!(properties.bit_depth(), 16); @@ -406,7 +404,7 @@ fn test_audio_properties() { // CPPUNIT_ASSERT_EQUAL(156556ULL, f.audioProperties()->sampleFrames()); assert_eq!( format!("{:X}", f.properties().signature()), - "6291DBD8DCB7DC480132E4C4BA154A17" + "CFE3D9DABADEAB2CBF2CA235274B7F76" ); } diff --git a/tests/taglib/test_mp4.rs b/tests/taglib/test_mp4.rs index c6cab2b1..1f9c448e 100644 --- a/tests/taglib/test_mp4.rs +++ b/tests/taglib/test_mp4.rs @@ -62,7 +62,7 @@ fn test_properties_alac() { #[test] #[allow(clippy::needless_range_loop)] fn test_properties_alac_without_bitrate() { - let mut file = temp_file!("tests/taglib/data/has-tags.m4a"); + let mut file = temp_file!("tests/taglib/data/empty_alac.m4a"); let mut alac_data = Vec::new(); file.read_to_end(&mut alac_data).unwrap(); @@ -386,7 +386,7 @@ fn test_repeated_save() { #[test] fn test_with_zero_length_atom() { - let mut file = temp_file!("tests/taglib/data/infloop.m4a"); + let mut file = temp_file!("tests/taglib/data/zero-length-mdat.m4a"); let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); assert_eq!(f.properties().duration().as_millis(), 1115); assert_eq!(f.properties().sample_rate(), 22050); diff --git a/tests/taglib/test_mpeg.rs b/tests/taglib/test_mpeg.rs index 7724a335..95abd1af 100644 --- a/tests/taglib/test_mpeg.rs +++ b/tests/taglib/test_mpeg.rs @@ -183,7 +183,7 @@ fn test_duplicate_id3v2() { #[test] fn test_fuzzed_file() { - let mut file = File::open("tests/taglib/data/invalid-frames3.mp3").unwrap(); + let mut file = File::open("tests/taglib/data/excessive_alloc.mp3").unwrap(); let _ = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); } From 16fc95ca8a5e4de1781365d37d8212da5a3fcc7b Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sun, 9 Jul 2023 13:55:44 -0400 Subject: [PATCH 32/75] Tests: Cleanup Taglib APE and AIFF tests --- tests/taglib/test_aiff.rs | 53 +++++++++++++++++---------------------- tests/taglib/test_ape.rs | 20 +++++++++++---- tests/taglib/util/mod.rs | 9 ------- 3 files changed, 38 insertions(+), 44 deletions(-) diff --git a/tests/taglib/test_aiff.rs b/tests/taglib/test_aiff.rs index 7da42d55..73e6b20a 100644 --- a/tests/taglib/test_aiff.rs +++ b/tests/taglib/test_aiff.rs @@ -1,17 +1,16 @@ -use lofty::{Accessor, AudioFile, FileType, ParseOptions, TaggedFileExt}; +use lofty::{Accessor, AudioFile, FileType, ParseOptions, Probe}; +use lofty::id3::v2::Id3v2Tag; use lofty::iff::aiff::AiffFile; -use std::io::{Read, Seek}; +use std::io::Seek; -use crate::util::get_filetype; +use crate::util::get_file; use crate::{assert_delta, temp_file}; #[test] #[ignore] fn test_aiff_properties() { - let file = lofty::read_from_path("tests/taglib/data/empty.aiff").unwrap(); - - assert_eq!(file.file_type(), FileType::Aiff); + let file = get_file::("tests/taglib/data/empty.aiff"); let properties = file.properties(); assert_eq!(properties.duration().as_secs(), 0); @@ -28,9 +27,7 @@ fn test_aiff_properties() { #[test] #[ignore] fn test_aifc_properties() { - let file = lofty::read_from_path("tests/taglib/data/alaw.aifc").unwrap(); - - assert_eq!(file.file_type(), FileType::Aiff); + let file = get_file::("tests/taglib/data/alaw.aifc"); let properties = file.properties(); assert_eq!(properties.duration().as_secs(), 0); @@ -52,15 +49,13 @@ fn test_save_id3v2() { let mut file = temp_file!("tests/taglib/data/empty.aiff"); { - let mut tfile = lofty::read_from(&mut file).unwrap(); + let mut tfile = AiffFile::read_from(&mut file, ParseOptions::new()).unwrap(); - assert_eq!(tfile.file_type(), FileType::Aiff); + assert!(tfile.id3v2().is_none()); - assert!(tfile.tag(lofty::TagType::Id3v2).is_none()); - - let mut tag = lofty::Tag::new(lofty::TagType::Id3v2); - tag.set_title("TitleXXX".to_string()); - tfile.insert_tag(tag); + let mut id3v2 = Id3v2Tag::new(); + id3v2.set_title("TitleXXX".to_string()); + tfile.set_id3v2(id3v2); file.rewind().unwrap(); tfile.save_to(&mut file).unwrap(); assert!(tfile.contains_tag_type(lofty::TagType::Id3v2)); @@ -69,14 +64,12 @@ fn test_save_id3v2() { file.rewind().unwrap(); { - let mut tfile = lofty::read_from(&mut file).unwrap(); - - assert_eq!(tfile.file_type(), FileType::Aiff); + let mut tfile = AiffFile::read_from(&mut file, ParseOptions::new()).unwrap(); - let mut tag = tfile.tag(lofty::TagType::Id3v2).unwrap().to_owned(); - assert_eq!(tag.title().as_deref(), Some("TitleXXX")); - tag.set_title(String::new()); - tfile.insert_tag(tag); + let mut id3v2 = tfile.id3v2().unwrap().to_owned(); + assert_eq!(id3v2.title().as_deref(), Some("TitleXXX")); + id3v2.set_title(String::new()); + tfile.set_id3v2(id3v2); file.rewind().unwrap(); tfile.save_to(&mut file).unwrap(); assert!(!tfile.contains_tag_type(lofty::TagType::Id3v2)); @@ -85,10 +78,7 @@ fn test_save_id3v2() { file.rewind().unwrap(); { - let tfile = lofty::read_from(&mut file).unwrap(); - - assert_eq!(tfile.file_type(), FileType::Aiff); - + let tfile = AiffFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert!(!tfile.contains_tag_type(lofty::TagType::Id3v2)); } } @@ -104,11 +94,14 @@ fn test_duplicate_id3v2() { } #[test] -#[ignore] fn test_fuzzed_file1() { assert_eq!( - get_filetype("tests/taglib/data/segfault.aif"), - FileType::Aiff + Probe::open("tests/taglib/data/segfault.aif") + .unwrap() + .guess_file_type() + .unwrap() + .file_type(), + Some(FileType::Aiff) ); } diff --git a/tests/taglib/test_ape.rs b/tests/taglib/test_ape.rs index 40d4f927..a71694bd 100644 --- a/tests/taglib/test_ape.rs +++ b/tests/taglib/test_ape.rs @@ -1,5 +1,4 @@ use crate::temp_file; -use crate::util::get_filetype; use std::fs::File; use std::io::Seek; @@ -7,7 +6,7 @@ use std::time::Duration; use lofty::ape::{ApeFile, ApeItem, ApeTag}; use lofty::id3::v1::Id3v1Tag; -use lofty::{Accessor, AudioFile, FileType, ItemValue, ParseOptions, TagExt}; +use lofty::{Accessor, AudioFile, FileType, ItemValue, ParseOptions, Probe, TagExt}; fn test_399(path: &str) { let mut file = File::open(path).unwrap(); @@ -79,14 +78,25 @@ fn test_properties_390() { #[test] fn test_fuzzed_file_1() { assert_eq!( - get_filetype("tests/taglib/data/longloop.ape"), - FileType::Ape + Probe::open("tests/taglib/data/longloop.ape") + .unwrap() + .guess_file_type() + .unwrap() + .file_type(), + Some(FileType::Ape) ); } #[test] fn test_fuzzed_file_2() { - assert_eq!(get_filetype("tests/taglib/data/zerodiv.ape"), FileType::Ape); + assert_eq!( + Probe::open("tests/taglib/data/zerodiv.ape") + .unwrap() + .guess_file_type() + .unwrap() + .file_type(), + Some(FileType::Ape) + ); } #[test] diff --git a/tests/taglib/util/mod.rs b/tests/taglib/util/mod.rs index 07f8bb5a..81042ce4 100644 --- a/tests/taglib/util/mod.rs +++ b/tests/taglib/util/mod.rs @@ -1,15 +1,6 @@ use lofty::{AudioFile, ParseOptions}; use std::fs::File; -/// This function tries to simulate TagLibs isValid function -// https://github.com/Serial-ATA/lofty-rs/pull/51#discussion_r873171570 -pub fn get_filetype>(path: P) -> lofty::FileType { - let mut file = std::fs::File::open(path).unwrap(); - let mut buf = [0; 12]; - std::io::Read::read_exact(&mut file, &mut buf).unwrap(); - lofty::FileType::from_buffer(&buf).unwrap() -} - pub fn get_file(path: &str) -> F { let mut file = File::open(path).unwrap(); F::read_from(&mut file, ParseOptions::new()).unwrap() From 295d49369517f4599a6117753d6a06bb22ee2b29 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sun, 9 Jul 2023 15:35:18 -0400 Subject: [PATCH 33/75] Tests: Stop creating temp files for immutable tests --- tests/taglib/test_aiff.rs | 10 +++++----- tests/taglib/test_ape.rs | 17 +++++++---------- tests/taglib/test_flac.rs | 10 ++++------ tests/taglib/test_id3v2.rs | 6 ++++-- tests/taglib/test_mp4.rs | 32 ++++++++++++-------------------- tests/taglib/test_mpeg.rs | 28 ++++++++++------------------ tests/taglib/test_ogaflac.rs | 4 +++- tests/taglib/test_ogg.rs | 8 ++++---- tests/taglib/test_opus.rs | 11 ++++++----- tests/taglib/test_speex.rs | 4 ++-- tests/taglib/test_synchdata.rs | 3 ++- tests/taglib/test_wav.rs | 19 +++++++------------ tests/taglib/test_wavpack.rs | 19 +++++++------------ tests/taglib/test_xiphcomment.rs | 4 +++- 14 files changed, 76 insertions(+), 99 deletions(-) diff --git a/tests/taglib/test_aiff.rs b/tests/taglib/test_aiff.rs index 73e6b20a..fbb3b9c3 100644 --- a/tests/taglib/test_aiff.rs +++ b/tests/taglib/test_aiff.rs @@ -1,11 +1,11 @@ -use lofty::{Accessor, AudioFile, FileType, ParseOptions, Probe}; +use crate::util::get_file; +use crate::{assert_delta, temp_file}; -use lofty::id3::v2::Id3v2Tag; -use lofty::iff::aiff::AiffFile; use std::io::Seek; -use crate::util::get_file; -use crate::{assert_delta, temp_file}; +use lofty::id3::v2::Id3v2Tag; +use lofty::iff::aiff::AiffFile; +use lofty::{Accessor, AudioFile, FileType, ParseOptions, Probe}; #[test] #[ignore] diff --git a/tests/taglib/test_ape.rs b/tests/taglib/test_ape.rs index a71694bd..de726152 100644 --- a/tests/taglib/test_ape.rs +++ b/tests/taglib/test_ape.rs @@ -1,6 +1,6 @@ use crate::temp_file; +use crate::util::get_file; -use std::fs::File; use std::io::Seek; use std::time::Duration; @@ -9,9 +9,8 @@ use lofty::id3::v1::Id3v1Tag; use lofty::{Accessor, AudioFile, FileType, ItemValue, ParseOptions, Probe, TagExt}; fn test_399(path: &str) { - let mut file = File::open(path).unwrap(); - let file = ApeFile::read_from(&mut file, ParseOptions::new()).unwrap(); - let properties = file.properties(); + let f = get_file::(path); + let properties = f.properties(); assert_eq!(properties.duration(), Duration::from_millis(3550)); assert_eq!(properties.bitrate(), 192); @@ -44,9 +43,8 @@ fn test_properties_399_id3v2() { #[test] #[ignore] fn test_properties_396() { - let mut file = File::open("tests/taglib/data/mac-396.ape").unwrap(); - let file = ApeFile::read_from(&mut file, ParseOptions::new()).unwrap(); - let properties = file.properties(); + let f = get_file::("tests/taglib/data/mac-396.ape"); + let properties = f.properties(); assert_eq!(properties.duration(), Duration::from_millis(3685)); assert_eq!(properties.bitrate(), 0); @@ -61,9 +59,8 @@ fn test_properties_396() { #[test] #[ignore] fn test_properties_390() { - let mut file = File::open("tests/taglib/data/mac-390-hdr.ape").unwrap(); - let file = ApeFile::read_from(&mut file, ParseOptions::new()).unwrap(); - let properties = file.properties(); + let f = get_file::("tests/taglib/data/mac-390-hdr.ape"); + let properties = f.properties(); assert_eq!(properties.duration(), Duration::from_millis(15630)); assert_eq!(properties.bitrate(), 0); diff --git a/tests/taglib/test_flac.rs b/tests/taglib/test_flac.rs index 86703b8d..46bb13d9 100644 --- a/tests/taglib/test_flac.rs +++ b/tests/taglib/test_flac.rs @@ -1,4 +1,5 @@ use crate::temp_file; +use crate::util::get_file; use std::io::{Read, Seek, SeekFrom}; @@ -11,8 +12,7 @@ use lofty::{ #[test] fn test_signature() { - let mut file = temp_file!("tests/taglib/data/no-tags.flac"); - let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/no-tags.flac"); assert_eq!( format!("{:x}", f.properties().signature()), "a1b141f766e9849ac3db1030a20a3c77" @@ -390,8 +390,7 @@ fn test_invalid() { #[test] fn test_audio_properties() { - let mut file = temp_file!("tests/taglib/data/sinewave.flac"); - let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/sinewave.flac"); let properties = f.properties(); assert_eq!(properties.duration().as_secs(), 3); @@ -410,8 +409,7 @@ fn test_audio_properties() { #[test] fn test_zero_sized_padding_1() { - let mut file = temp_file!("tests/taglib/data/zero-sized-padding.flac"); - let _ = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let _f = get_file::("tests/taglib/data/zero-sized-padding.flac"); } #[test] diff --git a/tests/taglib/test_id3v2.rs b/tests/taglib/test_id3v2.rs index 4637e811..49160a72 100644 --- a/tests/taglib/test_id3v2.rs +++ b/tests/taglib/test_id3v2.rs @@ -1,4 +1,8 @@ use crate::temp_file; + +use std::borrow::Cow; +use std::io::Seek; + use lofty::id3::v2::{ AttachedPictureFrame, CommentFrame, ExtendedTextFrame, ExtendedUrlFrame, Frame, FrameFlags, FrameId, FrameValue, GeneralEncapsulatedObject, Id3v2Tag, Id3v2Version, Popularimeter, @@ -10,8 +14,6 @@ use lofty::{ Accessor, AudioFile, MimeType, ParseOptions, ParsingMode, Picture, PictureType, TagExt, TextEncoding, }; -use std::borrow::Cow; -use std::io::Seek; #[test] fn test_unsynch_decode() { diff --git a/tests/taglib/test_mp4.rs b/tests/taglib/test_mp4.rs index 1f9c448e..7e821f82 100644 --- a/tests/taglib/test_mp4.rs +++ b/tests/taglib/test_mp4.rs @@ -1,4 +1,6 @@ use crate::temp_file; +use crate::util::get_file; + use std::borrow::Cow; use std::io::{Read, Seek}; @@ -7,8 +9,7 @@ use lofty::{Accessor, AudioFile, MimeType, ParseOptions, Picture, PictureType, T #[test] fn test_properties_aac() { - let mut file = temp_file!("tests/taglib/data/has-tags.m4a"); - let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/has-tags.m4a"); assert_eq!(f.properties().duration().as_secs(), 3); assert_eq!(f.properties().duration().as_millis(), 3708); assert_eq!(f.properties().audio_bitrate(), 3); @@ -47,8 +48,7 @@ fn test_properties_aac_without_bitrate() { #[test] fn test_properties_alac() { - let mut file = temp_file!("tests/taglib/data/empty_alac.m4a"); - let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/empty_alac.m4a"); assert_eq!(f.properties().duration().as_secs(), 3); assert_eq!(f.properties().duration().as_millis(), 3705); assert_eq!(f.properties().audio_bitrate(), 3); @@ -86,8 +86,7 @@ fn test_properties_alac_without_bitrate() { #[test] fn test_properties_m4v() { - let mut file = temp_file!("tests/taglib/data/blank_video.m4v"); - let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/blank_video.m4v"); assert_eq!(f.properties().duration().as_secs(), 0); assert_eq!(f.properties().duration().as_millis(), 975); assert_eq!(f.properties().audio_bitrate(), 96); @@ -107,8 +106,7 @@ fn test_check_valid() { #[test] fn test_has_tag() { { - let mut file = temp_file!("tests/taglib/data/has-tags.m4a"); - let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/has-tags.m4a"); assert!(f.ilst().is_some()); } @@ -241,15 +239,13 @@ fn test_64bit_atom() {} #[test] fn test_gnre() { - let mut file = temp_file!("tests/taglib/data/gnre.m4a"); - let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/gnre.m4a"); assert_eq!(f.ilst().unwrap().genre().as_deref(), Some("Ska")); } #[test] fn test_covr_read() { - let mut file = temp_file!("tests/taglib/data/has-tags.m4a"); - let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/has-tags.m4a"); let tag = f.ilst().unwrap(); assert!(tag.contains(&AtomIdent::Fourcc(*b"covr"))); let mut covrs = tag.get(&AtomIdent::Fourcc(*b"covr")).unwrap().data(); @@ -314,8 +310,7 @@ fn test_covr_write() { #[test] fn test_covr_read2() { - let mut file = temp_file!("tests/taglib/data/covr-junk.m4a"); - let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/covr-junk.m4a"); let tag = f.ilst().unwrap(); assert!(tag.contains(&AtomIdent::Fourcc(*b"covr"))); let mut covrs = tag.get(&AtomIdent::Fourcc(*b"covr")).unwrap().data(); @@ -353,8 +348,7 @@ fn test_properties_movement() { #[test] fn test_fuzzed_file() { - let mut file = temp_file!("tests/taglib/data/infloop.m4a"); - let _ = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + let _f = get_file::("tests/taglib/data/infloop.m4a"); } #[test] @@ -386,8 +380,7 @@ fn test_repeated_save() { #[test] fn test_with_zero_length_atom() { - let mut file = temp_file!("tests/taglib/data/zero-length-mdat.m4a"); - let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/zero-length-mdat.m4a"); assert_eq!(f.properties().duration().as_millis(), 1115); assert_eq!(f.properties().sample_rate(), 22050); } @@ -450,8 +443,7 @@ fn test_remove_metadata() { #[test] fn test_non_full_meta_atom() { - let mut file = temp_file!("tests/taglib/data/non-full-meta.m4a"); - let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/non-full-meta.m4a"); assert!(f.ilst().is_some()); let tag = f.ilst().unwrap(); diff --git a/tests/taglib/test_mpeg.rs b/tests/taglib/test_mpeg.rs index 95abd1af..836e7732 100644 --- a/tests/taglib/test_mpeg.rs +++ b/tests/taglib/test_mpeg.rs @@ -1,4 +1,5 @@ use crate::temp_file; +use crate::util::get_file; use std::fs::File; use std::io::Seek; @@ -12,8 +13,7 @@ use lofty::{Accessor, AudioFile, ParseOptions}; #[test] #[ignore] fn test_audio_properties_xing_header_cbr() { - let mut file = File::open("tests/taglib/data/lame_cbr.mp3").unwrap(); - let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/lame_cbr.mp3"); assert_eq!(f.properties().duration().as_secs(), 1887); // TODO: Off by 9 assert_eq!(f.properties().duration().as_millis(), 1887164); @@ -27,8 +27,7 @@ fn test_audio_properties_xing_header_cbr() { #[test] #[ignore] fn test_audio_properties_xing_header_vbr() { - let mut file = File::open("tests/taglib/data/lame_vbr.mp3").unwrap(); - let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/lame_vbr.mp3"); assert_eq!(f.properties().duration().as_secs(), 1887); // TODO: Off by 9 assert_eq!(f.properties().duration().as_millis(), 1887164); @@ -42,8 +41,7 @@ fn test_audio_properties_xing_header_vbr() { #[test] #[ignore] fn test_audio_properties_vbri_header() { - let mut file = File::open("tests/taglib/data/rare_frames.mp3").unwrap(); - let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/rare_frames.mp3"); assert_eq!(f.properties().duration().as_secs(), 222); // TODO: Off by 1 assert_eq!(f.properties().duration().as_millis(), 222198); @@ -57,8 +55,7 @@ fn test_audio_properties_vbri_header() { #[test] #[ignore] fn test_audio_properties_no_vbr_headers() { - let mut file = File::open("tests/taglib/data/bladeenc.mp3").unwrap(); - let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/bladeenc.mp3"); assert_eq!(f.properties().duration().as_secs(), 3); // Off by 1 assert_eq!(f.properties().duration().as_millis(), 3553); @@ -72,8 +69,7 @@ fn test_audio_properties_no_vbr_headers() { #[test] fn test_skip_invalid_frames_1() { - let mut file = File::open("tests/taglib/data/invalid-frames1.mp3").unwrap(); - let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/invalid-frames1.mp3"); assert_eq!(f.properties().duration().as_secs(), 0); assert_eq!(f.properties().duration().as_millis(), 392); @@ -85,8 +81,7 @@ fn test_skip_invalid_frames_1() { #[test] #[ignore] fn test_skip_invalid_frames_2() { - let mut file = File::open("tests/taglib/data/invalid-frames2.mp3").unwrap(); - let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/invalid-frames2.mp3"); assert_eq!(f.properties().duration().as_secs(), 0); assert_eq!(f.properties().duration().as_millis(), 314); // TODO: Off by 79 @@ -98,8 +93,7 @@ fn test_skip_invalid_frames_2() { #[test] #[ignore] fn test_skip_invalid_frames_3() { - let mut file = File::open("tests/taglib/data/invalid-frames3.mp3").unwrap(); - let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/invalid-frames3.mp3"); assert_eq!(f.properties().duration().as_secs(), 0); assert_eq!(f.properties().duration().as_millis(), 183); // TODO: Off by 26 @@ -111,8 +105,7 @@ fn test_skip_invalid_frames_3() { #[test] #[ignore] fn test_version_2_duration_with_xing_header() { - let mut file = File::open("tests/taglib/data/mpeg2.mp3").unwrap(); - let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/mpeg2.mp3"); assert_eq!(f.properties().duration().as_secs(), 5387); // TODO: Off by 15 assert_eq!(f.properties().duration().as_millis(), 5387285); } @@ -176,8 +169,7 @@ fn test_save_id3v23() { #[test] fn test_duplicate_id3v2() { - let mut file = File::open("tests/taglib/data/duplicate_id3v2.mp3").unwrap(); - let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/duplicate_id3v2.mp3"); assert_eq!(f.properties().sample_rate(), 44100); } diff --git a/tests/taglib/test_ogaflac.rs b/tests/taglib/test_ogaflac.rs index 80335ead..fe38b486 100644 --- a/tests/taglib/test_ogaflac.rs +++ b/tests/taglib/test_ogaflac.rs @@ -1,8 +1,10 @@ use crate::temp_file; + +use std::io::{Seek, SeekFrom}; + use lofty::flac::FlacFile; use lofty::ogg::VorbisComments; use lofty::{Accessor, AudioFile, ParseOptions}; -use std::io::{Seek, SeekFrom}; #[test] #[ignore] // TODO: We don't support FLAC in OGA diff --git a/tests/taglib/test_ogg.rs b/tests/taglib/test_ogg.rs index 5a86e319..dea861ac 100644 --- a/tests/taglib/test_ogg.rs +++ b/tests/taglib/test_ogg.rs @@ -1,10 +1,11 @@ use crate::temp_file; -use lofty::ogg::VorbisFile; -use lofty::{Accessor, AudioFile, ParseOptions}; +use crate::util::get_file; use std::io::{Read, Seek, SeekFrom}; use byteorder::{LittleEndian, ReadBytesExt}; +use lofty::ogg::VorbisFile; +use lofty::{Accessor, AudioFile, ParseOptions}; #[test] fn test_simple() { @@ -74,8 +75,7 @@ fn test_dict_interface2() { #[test] fn test_audio_properties() { - let mut file = temp_file!("tests/taglib/data/empty.ogg"); - let f = VorbisFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/empty.ogg"); assert_eq!(f.properties().duration().as_secs(), 3); assert_eq!(f.properties().duration().as_millis(), 3685); assert_eq!(f.properties().audio_bitrate(), 1); diff --git a/tests/taglib/test_opus.rs b/tests/taglib/test_opus.rs index 3253c786..ec55612e 100644 --- a/tests/taglib/test_opus.rs +++ b/tests/taglib/test_opus.rs @@ -1,12 +1,14 @@ use crate::temp_file; +use crate::util::get_file; + +use std::io::Seek; + use lofty::ogg::OpusFile; use lofty::{Accessor, AudioFile, ParseOptions}; -use std::io::Seek; #[test] fn test_audio_properties() { - let mut file = temp_file!("tests/taglib/data/correctness_gain_silent_output.opus"); - let f = OpusFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/correctness_gain_silent_output.opus"); assert_eq!(f.properties().duration().as_secs(), 7); assert_eq!(f.properties().duration().as_millis(), 7737); assert_eq!(f.properties().audio_bitrate(), 36); @@ -17,8 +19,7 @@ fn test_audio_properties() { #[test] fn test_read_comments() { - let mut file = temp_file!("tests/taglib/data/correctness_gain_silent_output.opus"); - let f = OpusFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/correctness_gain_silent_output.opus"); assert_eq!( f.vorbis_comments().get("ENCODER"), Some("Xiph.Org Opus testvectormaker") diff --git a/tests/taglib/test_speex.rs b/tests/taglib/test_speex.rs index dc81bc36..10cc332f 100644 --- a/tests/taglib/test_speex.rs +++ b/tests/taglib/test_speex.rs @@ -1,4 +1,5 @@ use crate::temp_file; +use crate::util::get_file; use std::io::Seek; @@ -8,8 +9,7 @@ use lofty::{Accessor, AudioFile, ParseOptions}; #[test] #[ignore] fn test_audio_properties() { - let mut file = temp_file!("tests/taglib/data/empty.spx"); - let f = SpeexFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/empty.spx"); assert_eq!(f.properties().duration().as_secs(), 3); // TODO: We report 3684, we're off by one diff --git a/tests/taglib/test_synchdata.rs b/tests/taglib/test_synchdata.rs index abd3863a..fa412b82 100644 --- a/tests/taglib/test_synchdata.rs +++ b/tests/taglib/test_synchdata.rs @@ -1,6 +1,7 @@ -use lofty::id3::v2::util::synchsafe::{SynchsafeInteger, UnsynchronizedStream}; use std::io::Read; +use lofty::id3::v2::util::synchsafe::{SynchsafeInteger, UnsynchronizedStream}; + #[test] fn test1() { let v = u32::from_be_bytes([0, 0, 0, 127]); diff --git a/tests/taglib/test_wav.rs b/tests/taglib/test_wav.rs index de609503..a2342492 100644 --- a/tests/taglib/test_wav.rs +++ b/tests/taglib/test_wav.rs @@ -1,4 +1,5 @@ use crate::temp_file; +use crate::util::get_file; use lofty::id3::v2::{Id3v2Tag, Id3v2Version}; use lofty::iff::wav::{RIFFInfoList, WavFile, WavFormat}; use lofty::{Accessor, AudioFile, ParseOptions, TagType}; @@ -6,8 +7,7 @@ use std::io::{Cursor, Read, Seek, SeekFrom, Write}; #[test] fn test_pcm_properties() { - let mut file = temp_file!("tests/taglib/data/empty.wav"); - let f = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/empty.wav"); assert_eq!(f.properties().duration().as_secs(), 3); assert_eq!(f.properties().duration().as_millis(), 3675); assert_eq!(f.properties().bitrate(), 32); @@ -20,8 +20,7 @@ fn test_pcm_properties() { #[test] fn test_alaw_properties() { - let mut file = temp_file!("tests/taglib/data/alaw.wav"); - let f = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/alaw.wav"); assert_eq!(f.properties().duration().as_secs(), 3); assert_eq!(f.properties().duration().as_millis(), 3550); assert_eq!(f.properties().bitrate(), 128); @@ -34,8 +33,7 @@ fn test_alaw_properties() { #[test] fn test_float_properties() { - let mut file = temp_file!("tests/taglib/data/float64.wav"); - let f = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/float64.wav"); assert_eq!(f.properties().duration().as_secs(), 0); assert_eq!(f.properties().duration().as_millis(), 97); assert_eq!(f.properties().bitrate(), 5645); @@ -257,8 +255,7 @@ fn test_duplicate_tags() { #[test] fn test_fuzzed_file1() { - let mut file = temp_file!("tests/taglib/data/infloop.wav"); - let f1 = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f1 = get_file::("tests/taglib/data/infloop.wav"); // The file has problems: // Chunk 'ISTt' has invalid size (larger than the file size). // Its properties can nevertheless be read. @@ -331,8 +328,7 @@ fn test_strip_and_properties() { #[test] fn test_pcm_with_fact_chunk() { - let mut file = temp_file!("tests/taglib/data/pcm_with_fact_chunk.wav"); - let f = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/pcm_with_fact_chunk.wav"); assert_eq!(f.properties().duration().as_secs(), 3); assert_eq!(f.properties().duration().as_millis(), 3675); assert_eq!(f.properties().bitrate(), 32); @@ -345,8 +341,7 @@ fn test_pcm_with_fact_chunk() { #[test] fn test_wave_format_extensible() { - let mut file = temp_file!("tests/taglib/data/uint8we.wav"); - let f = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/uint8we.wav"); assert_eq!(f.properties().duration().as_secs(), 2); assert_eq!(f.properties().duration().as_millis(), 2937); assert_eq!(f.properties().bitrate(), 128); diff --git a/tests/taglib/test_wavpack.rs b/tests/taglib/test_wavpack.rs index b2f931c2..288a364e 100644 --- a/tests/taglib/test_wavpack.rs +++ b/tests/taglib/test_wavpack.rs @@ -1,4 +1,5 @@ use crate::temp_file; +use crate::util::get_file; use std::io::Seek; @@ -9,8 +10,7 @@ use lofty::{Accessor, AudioFile, ParseOptions}; #[test] fn test_no_length_properties() { - let mut file = temp_file!("tests/taglib/data/no_length.wv"); - let f = WavPackFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/no_length.wv"); assert_eq!(f.properties().duration().as_secs(), 3); assert_eq!(f.properties().duration().as_millis(), 3705); assert_eq!(f.properties().audio_bitrate(), 1); @@ -24,8 +24,7 @@ fn test_no_length_properties() { #[test] fn test_multi_channel_properties() { - let mut file = temp_file!("tests/taglib/data/four_channels.wv"); - let f = WavPackFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/four_channels.wv"); assert_eq!(f.properties().duration().as_secs(), 3); assert_eq!(f.properties().duration().as_millis(), 3833); assert_eq!(f.properties().audio_bitrate(), 112); @@ -39,8 +38,7 @@ fn test_multi_channel_properties() { #[test] fn test_dsd_stereo_properties() { - let mut file = temp_file!("tests/taglib/data/dsd_stereo.wv"); - let f = WavPackFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/dsd_stereo.wv"); assert_eq!(f.properties().duration().as_secs(), 0); assert_eq!(f.properties().duration().as_millis(), 200); assert_eq!(f.properties().audio_bitrate(), 2096); @@ -54,8 +52,7 @@ fn test_dsd_stereo_properties() { #[test] fn test_non_standard_rate_properties() { - let mut file = temp_file!("tests/taglib/data/non_standard_rate.wv"); - let f = WavPackFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/non_standard_rate.wv"); assert_eq!(f.properties().duration().as_secs(), 3); assert_eq!(f.properties().duration().as_millis(), 3675); assert_eq!(f.properties().audio_bitrate(), 0); @@ -69,8 +66,7 @@ fn test_non_standard_rate_properties() { #[test] fn test_tagged_properties() { - let mut file = temp_file!("tests/taglib/data/tagged.wv"); - let f = WavPackFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = get_file::("tests/taglib/data/tagged.wv"); assert_eq!(f.properties().duration().as_secs(), 3); assert_eq!(f.properties().duration().as_millis(), 3550); assert_eq!(f.properties().audio_bitrate(), 172); @@ -84,8 +80,7 @@ fn test_tagged_properties() { #[test] fn test_fuzzed_file() { - let mut file = temp_file!("tests/taglib/data/infloop.wv"); - let _ = WavPackFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let _f = get_file::("tests/taglib/data/infloop.wv"); } #[test] diff --git a/tests/taglib/test_xiphcomment.rs b/tests/taglib/test_xiphcomment.rs index 8f222661..06811b7d 100644 --- a/tests/taglib/test_xiphcomment.rs +++ b/tests/taglib/test_xiphcomment.rs @@ -1,9 +1,11 @@ use crate::temp_file; + +use std::io::Seek; + use lofty::ogg::{OggPictureStorage, VorbisComments, VorbisFile}; use lofty::{ Accessor, AudioFile, MimeType, ParseOptions, Picture, PictureInformation, PictureType, TagExt, }; -use std::io::Seek; #[test] fn test_year() { From 1f7fa08b699b007bf9ac2ad5d165883178b19b70 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 10 Jul 2023 13:20:16 -0400 Subject: [PATCH 34/75] Tests: Switch ID3v1 collision test asset to MusePack --- tests/taglib/data/no-tags.mpc | Bin 0 -> 114 bytes tests/taglib/test_apetag.rs | 23 +++++++++++------------ 2 files changed, 11 insertions(+), 12 deletions(-) create mode 100644 tests/taglib/data/no-tags.mpc diff --git a/tests/taglib/data/no-tags.mpc b/tests/taglib/data/no-tags.mpc new file mode 100644 index 0000000000000000000000000000000000000000..3405545a29460883251a5ccd6135ae440bca5815 GIT binary patch literal 114 zcmeYbaP|)N;Crv7*~rn-9LK;a8RX8x=vo_f28=z~0~zHQg8eyM7=XYrfJKH(ED#*R L#wc_k*p(Ro^T!wZ literal 0 HcmV?d00001 diff --git a/tests/taglib/test_apetag.rs b/tests/taglib/test_apetag.rs index e8f5d8a0..628f22be 100644 --- a/tests/taglib/test_apetag.rs +++ b/tests/taglib/test_apetag.rs @@ -3,6 +3,7 @@ use crate::temp_file; use std::io::Seek; use lofty::ape::{ApeFile, ApeItem, ApeTag}; +use lofty::musepack::MpcFile; use lofty::{Accessor, AudioFile, ItemValue, ParseOptions, TagExt}; #[test] @@ -92,25 +93,23 @@ fn test_text_binary() { #[test] #[ignore] fn test_id3v1_collision() { - // TODO: This uses a musepack file in the TagLib test suite. It makes no difference for this test, but should - // be changed once Musepack is supported in Lofty. - let mut file = temp_file!("tests/taglib/data/no-tags.ape"); + let mut file = temp_file!("tests/taglib/data/no-tags.mpc"); { - let mut ape_file = - ApeFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); - assert!(ape_file.ape().is_none()); - assert!(ape_file.id3v1().is_none()); + let mut mpc_file = + MpcFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); + assert!(mpc_file.ape().is_none()); + assert!(mpc_file.id3v1().is_none()); let mut ape_tag = ApeTag::default(); ape_tag.set_artist(String::from("Filltointersect ")); ape_tag.set_title(String::from("Filltointersect ")); - ape_file.set_ape(ape_tag); - ape_file.save_to(&mut file).unwrap(); + mpc_file.set_ape(ape_tag); + mpc_file.save_to(&mut file).unwrap(); } { file.rewind().unwrap(); - let ape_file = - ApeFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); - assert!(ape_file.id3v1().is_none()); + let mpc_file = + MpcFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); + assert!(mpc_file.id3v1().is_none()); } } From 7eb968c2a8ab62bd1796ccdc5081873fa4299d46 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 10 Jul 2023 13:21:30 -0400 Subject: [PATCH 35/75] Tests: Remove stray `todo!()`s in resolver test --- tests/taglib/test_fileref.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/taglib/test_fileref.rs b/tests/taglib/test_fileref.rs index c2e2e89d..d2d25ff7 100644 --- a/tests/taglib/test_fileref.rs +++ b/tests/taglib/test_fileref.rs @@ -243,19 +243,19 @@ fn test_file_resolver() { } fn save_to(&self, _: &mut File) -> lofty::Result<()> { - todo!() + unimplemented!() } fn properties(&self) -> &Self::Properties { - todo!() + unimplemented!() } fn contains_tag(&self) -> bool { - todo!() + unimplemented!() } fn contains_tag_type(&self, _: TagType) -> bool { - todo!() + unimplemented!() } } @@ -265,11 +265,11 @@ fn test_file_resolver() { } fn primary_tag_type() -> TagType { - todo!() + unimplemented!() } fn supported_tag_types() -> &'static [TagType] { - todo!() + unimplemented!() } fn guess(_: &[u8]) -> Option { From c266b604e2f230601de87142ab1d49320d20ae3f Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 10 Jul 2023 13:33:17 -0400 Subject: [PATCH 36/75] Tests: Remove TagLib ID3v1 `test_strip_whitespace` test --- tests/taglib/test_id3v1.rs | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/tests/taglib/test_id3v1.rs b/tests/taglib/test_id3v1.rs index 948c1cc5..b9016b23 100644 --- a/tests/taglib/test_id3v1.rs +++ b/tests/taglib/test_id3v1.rs @@ -1,29 +1,9 @@ -use crate::temp_file; - -use std::io::Seek; - -use lofty::id3::v1::{Id3v1Tag, GENRES}; -use lofty::mpeg::MpegFile; -use lofty::{Accessor, AudioFile, ParseOptions}; +use lofty::id3::v1::GENRES; #[test] -#[ignore] // TODO: We probably should be stripping whitespace +#[ignore] fn test_strip_whitespace() { - let mut file = temp_file!("tests/taglib/data/xing.mp3"); - { - let mut f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); - file.rewind().unwrap(); - - let mut tag = Id3v1Tag::default(); - tag.set_artist(String::from("Artist ")); - f.set_id3v1(tag); - f.save_to(&mut file).unwrap(); - } - file.rewind().unwrap(); - { - let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); - assert_eq!(f.id3v1().unwrap().artist().as_deref(), Some("Artist")); - } + // Marker test, we'd be overstepping to remove trailing whitespace that may be intentional } #[test] From e9d813094efd6049cc84d41cdb8e303c94683fb5 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 10 Jul 2023 18:43:32 -0400 Subject: [PATCH 37/75] Tests: Update TagLib AIFF tests --- tests/taglib/test_aiff.rs | 45 ++++++++++++++++++++++--------------- tests/taglib/test_apetag.rs | 2 +- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/tests/taglib/test_aiff.rs b/tests/taglib/test_aiff.rs index fbb3b9c3..4c61db2f 100644 --- a/tests/taglib/test_aiff.rs +++ b/tests/taglib/test_aiff.rs @@ -4,47 +4,55 @@ use crate::{assert_delta, temp_file}; use std::io::Seek; use lofty::id3::v2::Id3v2Tag; -use lofty::iff::aiff::AiffFile; +use lofty::iff::aiff::{AiffCompressionType, AiffFile}; use lofty::{Accessor, AudioFile, FileType, ParseOptions, Probe}; #[test] -#[ignore] fn test_aiff_properties() { let file = get_file::("tests/taglib/data/empty.aiff"); let properties = file.properties(); assert_eq!(properties.duration().as_secs(), 0); assert_delta!(properties.duration().as_millis(), 67, 1); - assert_delta!(properties.audio_bitrate().unwrap(), 706, 1); - assert_eq!(properties.sample_rate(), Some(44100)); - assert_eq!(properties.channels(), Some(1)); - assert_eq!(properties.bit_depth(), Some(16)); + assert_delta!(properties.audio_bitrate(), 706, 1); + assert_eq!(properties.sample_rate(), 44100); + assert_eq!(properties.channels(), 1); + assert_eq!(properties.sample_size(), 16); // TODO: get those options in lofty // CPPUNIT_ASSERT_EQUAL(2941U, f.audioProperties()->sampleFrames()); - // CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isAiffC()); + assert!(properties.compression_type().is_none()); } #[test] -#[ignore] fn test_aifc_properties() { let file = get_file::("tests/taglib/data/alaw.aifc"); let properties = file.properties(); assert_eq!(properties.duration().as_secs(), 0); assert_delta!(properties.duration().as_millis(), 37, 1); - assert_eq!(properties.audio_bitrate(), Some(355)); - assert_eq!(properties.sample_rate(), Some(44100)); - assert_eq!(properties.channels(), Some(1)); - assert_eq!(properties.bit_depth(), Some(16)); + assert_eq!(properties.audio_bitrate(), 355); + assert_eq!(properties.sample_rate(), 44100); + assert_eq!(properties.channels(), 1); + assert_eq!(properties.sample_size(), 16); // TODO: get those options in lofty // CPPUNIT_ASSERT_EQUAL(1622U, f.audioProperties()->sampleFrames()); - // CPPUNIT_ASSERT_EQUAL(true, f.audioProperties()->isAiffC()); - // CPPUNIT_ASSERT_EQUAL(ByteVector("ALAW"), f.audioProperties()->compressionType()); - // CPPUNIT_ASSERT_EQUAL(String("SGI CCITT G.711 A-law"), f.audioProperties()->compressionName()); + assert!(properties.compression_type().is_some()); + assert_eq!( + properties.compression_type().unwrap().clone(), + AiffCompressionType::ALAW + ); + // NOTE: The file's compression name is actually "SGI CCITT G.711 A-law" + // + // We have a hardcoded value for any of the concrete AiffCompressionType variants, as the messages + // are more or less standardized. This is not that big of a deal, especially as many encoders choose + // not to even write a compression name in the first place. + assert_eq!( + properties.compression_type().unwrap().compression_name(), + "CCITT G.711 A-law" + ); } #[test] -#[ignore] fn test_save_id3v2() { let mut file = temp_file!("tests/taglib/data/empty.aiff"); @@ -68,11 +76,12 @@ fn test_save_id3v2() { let mut id3v2 = tfile.id3v2().unwrap().to_owned(); assert_eq!(id3v2.title().as_deref(), Some("TitleXXX")); - id3v2.set_title(String::new()); + // NOTE: TagLib sets an empty title, which will implicitly remove it from the tag. Lofty will allow empty tag items to exist. + // What's important is that these are equivalent in behavior. + id3v2.remove_title(); tfile.set_id3v2(id3v2); file.rewind().unwrap(); tfile.save_to(&mut file).unwrap(); - assert!(!tfile.contains_tag_type(lofty::TagType::Id3v2)); } file.rewind().unwrap(); diff --git a/tests/taglib/test_apetag.rs b/tests/taglib/test_apetag.rs index 628f22be..2bc591ab 100644 --- a/tests/taglib/test_apetag.rs +++ b/tests/taglib/test_apetag.rs @@ -2,7 +2,7 @@ use crate::temp_file; use std::io::Seek; -use lofty::ape::{ApeFile, ApeItem, ApeTag}; +use lofty::ape::{ApeItem, ApeTag}; use lofty::musepack::MpcFile; use lofty::{Accessor, AudioFile, ItemValue, ParseOptions, TagExt}; From 884a72e107a24a6f9237a1d1a8d7e022d07a9c95 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 24 Jul 2023 15:37:07 -0400 Subject: [PATCH 38/75] Tests: Update TagLib ID3v2 tests for new frames --- tests/taglib/test_id3v2.rs | 191 ++++++++++++++++++++++++++++++++----- 1 file changed, 168 insertions(+), 23 deletions(-) diff --git a/tests/taglib/test_id3v2.rs b/tests/taglib/test_id3v2.rs index 49160a72..cf58feab 100644 --- a/tests/taglib/test_id3v2.rs +++ b/tests/taglib/test_id3v2.rs @@ -1,13 +1,15 @@ use crate::temp_file; use std::borrow::Cow; +use std::collections::HashMap; use std::io::Seek; use lofty::id3::v2::{ - AttachedPictureFrame, CommentFrame, ExtendedTextFrame, ExtendedUrlFrame, Frame, FrameFlags, - FrameId, FrameValue, GeneralEncapsulatedObject, Id3v2Tag, Id3v2Version, Popularimeter, - SyncTextContentType, SynchronizedText, TimestampFormat, UniqueFileIdentifierFrame, - UrlLinkFrame, + AttachedPictureFrame, ChannelInformation, ChannelType, CommentFrame, Event, + EventTimingCodesFrame, EventType, ExtendedTextFrame, ExtendedUrlFrame, Frame, FrameFlags, + FrameId, FrameValue, GeneralEncapsulatedObject, Id3v2Tag, Id3v2Version, OwnershipFrame, + Popularimeter, PrivateFrame, RelativeVolumeAdjustmentFrame, SyncTextContentType, + SynchronizedText, TimestampFormat, UniqueFileIdentifierFrame, UrlLinkFrame, }; use lofty::mpeg::MpegFile; use lofty::{ @@ -264,12 +266,61 @@ fn test_popm_from_file() { } #[test] -#[ignore] // TODO: We don't support RVA2 frames yet -fn test_parse_relative_volume_frame() {} +fn test_parse_relative_volume_frame() { + let f = RelativeVolumeAdjustmentFrame::parse( + &mut &b"\ + ident\x00\ + \x02\ + \x00\x0F\ + \x08\ + \x45"[..], + ParsingMode::Strict, + ) + .unwrap() + .unwrap(); -#[test] -#[ignore] // TODO: We don't support RVA2 frames yet -fn test_render_relative_volume_frame() {} + assert_eq!(f.identification, "ident"); + let front_right = f.channels.get(&ChannelType::FrontRight).unwrap(); + assert_eq!( + front_right.volume_adjustment as f32 / 512.0f32, + 15.0f32 / 512.0f32 + ); + assert_eq!(front_right.volume_adjustment, 15); + assert_eq!(front_right.bits_representing_peak, 8); + assert_eq!(front_right.peak_volume, Some(vec![0x45])); + let channels = f.channels; + assert_eq!(channels.len(), 1); +} + +#[test] +fn test_render_relative_volume_frame() { + let f = RelativeVolumeAdjustmentFrame { + identification: String::from("ident"), + channels: { + let mut m = HashMap::new(); + m.insert( + ChannelType::FrontRight, + ChannelInformation { + channel_type: ChannelType::FrontRight, + volume_adjustment: 15, + bits_representing_peak: 8, + peak_volume: Some(vec![0x45]), + }, + ); + m + }, + }; + + assert_eq!( + f.as_bytes(), + b"\ + ident\x00\ + \x02\ + \x00\x0F\ + \x08\ + \x45" + ); +} #[test] fn test_parse_unique_file_identifier_frame() { @@ -364,12 +415,40 @@ fn test_render_user_url_link_frame() { } #[test] -#[ignore] // TODO: We don't support OWNE frames yet -fn test_parse_ownership_frame() {} +fn test_parse_ownership_frame() { + let f = OwnershipFrame::parse( + &mut &b"\ + \x00\ + GBP1.99\x00\ + 20120905\ + Beatport"[..], + ) + .unwrap() + .unwrap(); + + assert_eq!(f.price_paid, "GBP1.99"); + assert_eq!(f.date_of_purchase, "20120905"); + assert_eq!(f.seller, "Beatport"); +} #[test] -#[ignore] // TODO: We don't support OWNE frames yet -fn test_render_ownership_frame() {} +fn test_render_ownership_frame() { + let f = OwnershipFrame { + encoding: TextEncoding::Latin1, + price_paid: String::from("GBP1.99"), + date_of_purchase: String::from("20120905"), + seller: String::from("Beatport"), + }; + + assert_eq!( + f.as_bytes().unwrap(), + b"\ + \x00\ + GBP1.99\x00\ + 20120905\ + Beatport"[..] + ) +} #[test] fn test_parse_synchronized_lyrics_frame() { @@ -459,12 +538,54 @@ fn test_render_synchronized_lyrics_frame() { } #[test] -#[ignore] // TODO: We don't support ETCO frames yet -fn test_parse_event_timing_codes_frame() {} +fn test_parse_event_timing_codes_frame() { + let f = EventTimingCodesFrame::parse( + &mut &b"\ + \x02\ + \x02\ + \x00\x00\xf3\x5c\ + \xfe\ + \x00\x36\xee\x80"[..], + ) + .unwrap() + .unwrap(); + + assert_eq!(f.timestamp_format, TimestampFormat::MS); + + let sel = f.events; + assert_eq!(sel.len(), 2); + assert_eq!(sel[0].event_type, EventType::IntroStart); + assert_eq!(sel[0].timestamp, 62300); + assert_eq!(sel[1].event_type, EventType::AudioFileEnds); + assert_eq!(sel[1].timestamp, 3600000); +} #[test] -#[ignore] // TODO: We don't support ETCO frames yet -fn test_render_event_timing_codes_frame() {} +fn test_render_event_timing_codes_frame() { + let f = EventTimingCodesFrame { + timestamp_format: TimestampFormat::MS, + events: vec![ + Event { + event_type: EventType::IntroStart, + timestamp: 62300, + }, + Event { + event_type: EventType::AudioFileEnds, + timestamp: 3600000, + }, + ], + }; + + assert_eq!( + f.as_bytes(), + b"\ + \x02\ + \x02\ + \x00\x00\xf3\x5c\ + \xfe\ + \x00\x36\xee\x80" + ) +} #[test] fn test_parse_comments_frame() { @@ -516,12 +637,33 @@ fn test_render_podcast_frame() { } #[test] -#[ignore] // TODO: We don't support PRIV frames yet -fn test_parse_private_frame() {} +fn test_parse_private_frame() { + let f = PrivateFrame::parse( + &mut &b"\ + WM/Provider\x00\ + TL"[..], + ) + .unwrap() + .unwrap(); + + assert_eq!(f.owner, "WM/Provider"); + assert_eq!(f.private_data, b"TL"); +} #[test] -#[ignore] // TODO: We don't support PRIV frames yet -fn test_render_private_frame() {} +fn test_render_private_frame() { + let f = PrivateFrame { + owner: String::from("WM/Provider"), + private_data: b"TL".to_vec(), + }; + + assert_eq!( + f.as_bytes(), + b"\ + WM/Provider\x00\ + TL" + ); +} #[test] fn test_parse_user_text_identification_frame() { @@ -747,7 +889,7 @@ fn test_delete_frame() { file.rewind().unwrap(); let t = f.id3v2_mut().unwrap(); - t.remove("TCON"); + let _ = t.remove(&FrameId::Valid(Cow::Borrowed("TCON"))); f.save_to(&mut file).unwrap(); } file.rewind().unwrap(); @@ -776,7 +918,10 @@ fn test_save_and_strip_id3v1_should_not_add_frame_from_id3v1_to_id3v2() { let mut bar = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); - bar.id3v2_mut().unwrap().remove("TPE1"); + let _ = bar + .id3v2_mut() + .unwrap() + .remove(&FrameId::Valid(Cow::Borrowed("TPE1"))); bar.save_to(&mut file).unwrap(); } From 60f6c28f3600454eb67c36dd42a3fef59d938efd Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 24 Jul 2023 15:49:57 -0400 Subject: [PATCH 39/75] Tests: Fix TagLib W000 ID3v2 test --- tests/taglib/test_id3v2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/taglib/test_id3v2.rs b/tests/taglib/test_id3v2.rs index cf58feab..e835b2e7 100644 --- a/tests/taglib/test_id3v2.rs +++ b/tests/taglib/test_id3v2.rs @@ -840,7 +840,7 @@ fn test_compressed_frame_with_broken_length() { #[test] fn test_w000() { let mut file = temp_file!("tests/taglib/data/w000.mp3"); - let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = MpegFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); assert!(f .id3v2() From ebd6200174df6319bcb4baba06b6fa14c1efed1d Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 24 Jul 2023 15:56:38 -0400 Subject: [PATCH 40/75] Tests: Fix TagLib TestMPEG::testFuzzedFile The behavior here is weird, the file contains multiple strings that are completely broken. Somehow, TagLib considers the file to be "valid", with it containing no MPEG frames. That doesn't seem correct on their end. --- tests/taglib/test_mpeg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/taglib/test_mpeg.rs b/tests/taglib/test_mpeg.rs index 836e7732..32fda916 100644 --- a/tests/taglib/test_mpeg.rs +++ b/tests/taglib/test_mpeg.rs @@ -176,7 +176,7 @@ fn test_duplicate_id3v2() { #[test] fn test_fuzzed_file() { let mut file = File::open("tests/taglib/data/excessive_alloc.mp3").unwrap(); - let _ = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(MpegFile::read_from(&mut file, ParseOptions::new()).is_err()) } #[test] From 0eddd8856323bead24410176ff1be530abde8227 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 24 Jul 2023 16:58:03 -0400 Subject: [PATCH 41/75] Tests: Ignore TagLib TestOGG::testPageChecksum for now --- tests/taglib/test_ogg.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/taglib/test_ogg.rs b/tests/taglib/test_ogg.rs index dea861ac..a9343308 100644 --- a/tests/taglib/test_ogg.rs +++ b/tests/taglib/test_ogg.rs @@ -88,6 +88,7 @@ fn test_audio_properties() { } #[test] +#[ignore] // TODO: Need to look into this one, not sure why there's a difference in checksums fn test_page_checksum() { let mut file = temp_file!("tests/taglib/data/empty.ogg"); From 2a5ee432e266168cb0d13574381c3f9421715aa8 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 27 Jul 2023 03:21:16 -0400 Subject: [PATCH 42/75] Tests: Ignore testFLAC::testMultipleCommentBlocks --- tests/taglib/test_flac.rs | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/tests/taglib/test_flac.rs b/tests/taglib/test_flac.rs index 46bb13d9..1b9073e5 100644 --- a/tests/taglib/test_flac.rs +++ b/tests/taglib/test_flac.rs @@ -20,29 +20,11 @@ fn test_signature() { } #[test] +#[ignore] fn test_multiple_comment_blocks() { - let mut file = temp_file!("tests/taglib/data/multiple-vc.flac"); - { - let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); - file.rewind().unwrap(); - - assert_eq!( - f.vorbis_comments().unwrap().artist().as_deref(), - Some("Artist 1") - ); - f.vorbis_comments_mut() - .unwrap() - .set_artist(String::from("The Artist")); - f.save_to(&mut file).unwrap(); - } - file.rewind().unwrap(); - { - let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); - assert_eq!( - f.vorbis_comments().unwrap().artist().as_deref(), - Some("The Artist") - ); - } + // Marker test, Lofty does not replicate TagLib's behavior + // + // TagLib will use the *first* tag in the stream, while we use the latest. } #[test] From 75f3108bc0ca7b93f350702180ae097e8c749316 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 27 Jul 2023 12:59:23 -0400 Subject: [PATCH 43/75] Tests: Unignore TagLib tests, replacing with todo!() panics --- tests/taglib/test_aiff.rs | 5 +- tests/taglib/test_ape.rs | 5 -- tests/taglib/test_apetag.rs | 1 - tests/taglib/test_fileref.rs | 4 +- tests/taglib/test_flac.rs | 6 +- tests/taglib/test_id3v2.rs | 67 +++++++++++-------- tests/taglib/test_mp4.rs | 9 ++- tests/taglib/test_mpeg.rs | 12 +--- tests/taglib/test_ogaflac.rs | 52 +++++++-------- tests/taglib/test_ogg.rs | 2 +- tests/taglib/test_speex.rs | 3 +- tests/taglib/test_wav.rs | 126 +++++++++++++++++------------------ 12 files changed, 148 insertions(+), 144 deletions(-) diff --git a/tests/taglib/test_aiff.rs b/tests/taglib/test_aiff.rs index 4c61db2f..c1d15c00 100644 --- a/tests/taglib/test_aiff.rs +++ b/tests/taglib/test_aiff.rs @@ -93,8 +93,9 @@ fn test_save_id3v2() { } #[test] -#[ignore] // TODO: Support writing ID3v2.3 tags -fn test_save_id3v23() {} +fn test_save_id3v23() { + todo!("Support writing ID3v2.3 tags") +} #[test] #[ignore] diff --git a/tests/taglib/test_ape.rs b/tests/taglib/test_ape.rs index de726152..09c74d8a 100644 --- a/tests/taglib/test_ape.rs +++ b/tests/taglib/test_ape.rs @@ -23,25 +23,21 @@ fn test_399(path: &str) { } #[test] -#[ignore] fn test_properties_399() { test_399("tests/taglib/data/mac-399.ape") } #[test] -#[ignore] fn test_properties_399_tagged() { test_399("tests/taglib/data/mac-399-tagged.ape") } #[test] -#[ignore] fn test_properties_399_id3v2() { test_399("tests/taglib/data/mac-399-id3v2.ape") } #[test] -#[ignore] fn test_properties_396() { let f = get_file::("tests/taglib/data/mac-396.ape"); let properties = f.properties(); @@ -57,7 +53,6 @@ fn test_properties_396() { } #[test] -#[ignore] fn test_properties_390() { let f = get_file::("tests/taglib/data/mac-390-hdr.ape"); let properties = f.properties(); diff --git a/tests/taglib/test_apetag.rs b/tests/taglib/test_apetag.rs index 2bc591ab..bc611624 100644 --- a/tests/taglib/test_apetag.rs +++ b/tests/taglib/test_apetag.rs @@ -91,7 +91,6 @@ fn test_text_binary() { // TODO: Does not work! We fall for this collision. #[test] -#[ignore] fn test_id3v1_collision() { let mut file = temp_file!("tests/taglib/data/no-tags.mpc"); { diff --git a/tests/taglib/test_fileref.rs b/tests/taglib/test_fileref.rs index d2d25ff7..174c77aa 100644 --- a/tests/taglib/test_fileref.rs +++ b/tests/taglib/test_fileref.rs @@ -78,7 +78,6 @@ fn file_ref_save(path: &str, expected_file_type: FileType) { } #[test] -#[ignore] fn test_musepack() { file_ref_save("click.mpc", FileType::Mpc); } @@ -202,7 +201,6 @@ fn test_create() { } #[test] -#[ignore] // TODO: We're off by over 200ms fn test_audio_properties() { let file = lofty::read_from_path("tests/taglib/data/xing.mp3").unwrap(); let properties = file.properties(); @@ -216,8 +214,8 @@ fn test_default_file_extensions() { // Marker test, Lofty does not replicate this API } +// TODO: We need to check resolvers *first* and then resort to our default implementations #[test] -#[ignore] // TODO: We need to check resolvers *first* and then resort to our default implementations fn test_file_resolver() { { let file = lofty::read_from_path("tests/taglib/data/xing.mp3").unwrap(); diff --git a/tests/taglib/test_flac.rs b/tests/taglib/test_flac.rs index 1b9073e5..6dd9bcf0 100644 --- a/tests/taglib/test_flac.rs +++ b/tests/taglib/test_flac.rs @@ -219,8 +219,8 @@ fn test_repeated_save_2() { // Marker test, this test relies on saving an ID3v2 tag in a FLAC file, something Lofty does not and will not support. } +// TODO: We don't make use of padding blocks yet #[test] -#[ignore] // TODO: We don't make use of padding blocks yet fn test_repeated_save_3() { let mut file = temp_file!("tests/taglib/data/no-tags.flac"); @@ -422,8 +422,8 @@ fn test_zero_sized_padding_2() { } } +// TODO: We don't make use of padding blocks yet #[test] -#[ignore] // TODO: We don't make use of padding blocks yet fn test_shrink_padding() { let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); { @@ -478,8 +478,8 @@ fn test_empty_id3v2() { } } +// TODO: TagLib doesn't fully remove Vorbis Comments when stripping. It will preserve the vendor string. Should we do the same? #[test] -#[ignore] // TODO: TagLib doesn't fully remove Vorbis Comments when stripping. It will preserve the vendor string. Should we do the same? fn test_strip_tags() { // NOTE: In the TagLib test suite, this also tests ID3v1 and ID3v2. That is not replicated here. diff --git a/tests/taglib/test_id3v2.rs b/tests/taglib/test_id3v2.rs index e835b2e7..f4ba659f 100644 --- a/tests/taglib/test_id3v2.rs +++ b/tests/taglib/test_id3v2.rs @@ -30,20 +30,24 @@ fn test_unsynch_decode() { } #[test] -#[ignore] // TODO: We don't support downgrading 2.4 tags to 2.3 -fn test_downgrade_utf8_for_id3v23_1() {} +fn test_downgrade_utf8_for_id3v23_1() { + todo!("We don't support downgrading 2.4 tags to 2.3") +} #[test] -#[ignore] // TODO: We don't support downgrading 2.4 tags to 2.3 -fn test_downgrade_utf8_for_id3v23_2() {} +fn test_downgrade_utf8_for_id3v23_2() { + todo!("We don't support downgrading 2.4 tags to 2.3") +} #[test] -#[ignore] // TODO: Need to think of a nice way to handle multiple UTF-16 values separated by null -fn test_utf16be_delimiter() {} +fn test_utf16be_delimiter() { + todo!("Need to think of a nice way to handle multiple UTF-16 values separated by null") +} #[test] -#[ignore] // TODO: Need to think of a nice way to handle multiple UTF-16 values separated by null -fn test_utf16_delimiter() {} +fn test_utf16_delimiter() { + todo!("Need to think of a nice way to handle multiple UTF-16 values separated by null") +} #[test] fn test_broken_frame1() { @@ -723,8 +727,8 @@ fn test_render_user_text_identification_frame() { ); } +// TODO: iTunes, being the great application it is writes unsynchronized integers for sizes. There's no *great* way to detect this. #[test] -#[ignore] // TODO: iTunes, being the great application it is writes unsynchronized integers for sizes. There's no *great* way to detect this. fn test_itunes_24_frame_size() { let mut file = temp_file!("tests/taglib/data/005411.id3"); let f = MpegFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); @@ -776,8 +780,9 @@ fn test_save_utf16_comment() { } #[test] -#[ignore] // TODO: We don't support downgrading to 2.3 tags yet -fn test_update_genre_23_1() {} +fn test_update_genre_23_1() { + todo!("We don't support downgrading to 2.3 tags yet") +} #[test] #[ignore] @@ -792,8 +797,12 @@ fn test_update_genre23_3() { } #[test] -#[ignore] // TODO: We currently just return the genre string as it is in the tag, need to think about whether or not to convert numerical strings -fn test_update_genre_24() {} +fn test_update_genre_24() { + todo!( + "We currently just return the genre string as it is in the tag, need to think about \ + whether or not to convert numerical strings" + ) +} #[test] fn test_update_date22() { @@ -803,8 +812,8 @@ fn test_update_date22() { assert_eq!(f.id3v2().unwrap().year(), Some(2010)); } +// TODO: Determine if this is even worth doing. It is just combining TYE+TDA when upgrading ID3v2.2 to 2.4 #[test] -#[ignore] // TODO: Determine if this is even worth doing. It is just combining TYE+TDA when upgrading ID3v2.2 to 2.4 fn test_update_full_date22() { let mut file = temp_file!("tests/taglib/data/id3v22-tda.mp3"); let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); @@ -813,8 +822,9 @@ fn test_update_full_date22() { } #[test] -#[ignore] // TODO: We don't support downgrading 2.4 tags to 2.3 -fn test_downgrade_to_23() {} +fn test_downgrade_to_23() { + todo!("We don't support downgrading 2.4 tags to 2.3") +} #[test] fn test_compressed_frame_with_broken_length() { @@ -932,20 +942,24 @@ fn test_save_and_strip_id3v1_should_not_add_frame_from_id3v1_to_id3v2() { } #[test] -#[ignore] // TODO: We don't support CHAP frames yet -fn test_parse_chapter_frame() {} +fn test_parse_chapter_frame() { + todo!("We don't support CHAP frames yet") +} #[test] -#[ignore] // TODO: We don't support CHAP frames yet -fn test_render_chapter_frame() {} +fn test_render_chapter_frame() { + todo!("We don't support CHAP frames yet") +} #[test] -#[ignore] // TODO: We don't support CTOC frames yet -fn test_parse_table_of_contents_frame() {} +fn test_parse_table_of_contents_frame() { + todo!("We don't support CTOC frames yet") +} #[test] -#[ignore] // TODO: We don't support CTOC frames yet -fn test_render_table_of_contents_frame() {} +fn test_render_table_of_contents_frame() { + todo!("We don't support CTOC frames yet") +} #[test] #[ignore] @@ -960,5 +974,6 @@ fn test_duplicate_tags() { } #[test] -#[ignore] // TODO: We don't support CTOC frames yet -fn test_parse_toc_frame_with_many_children() {} +fn test_parse_toc_frame_with_many_children() { + todo!("We don't support CTOC frames yet") +} diff --git a/tests/taglib/test_mp4.rs b/tests/taglib/test_mp4.rs index 7e821f82..bab6d8ea 100644 --- a/tests/taglib/test_mp4.rs +++ b/tests/taglib/test_mp4.rs @@ -139,7 +139,7 @@ fn test_is_empty() { #[test] fn test_update_stco() { - // TODO: We don't update stco atoms + todo!("We don't update stco atoms") } #[test] @@ -234,8 +234,11 @@ fn test_save_existing_when_ilst_is_last() { } #[test] -#[ignore] // TODO: Maybe? This just checks the moov atom's length. We don't retain any atoms we don't need. -fn test_64bit_atom() {} +fn test_64bit_atom() { + todo!( + "Maybe? This just checks the moov atom's length. We don't retain any atoms we don't need." + ) +} #[test] fn test_gnre() { diff --git a/tests/taglib/test_mpeg.rs b/tests/taglib/test_mpeg.rs index 32fda916..d3dda286 100644 --- a/tests/taglib/test_mpeg.rs +++ b/tests/taglib/test_mpeg.rs @@ -11,7 +11,6 @@ use lofty::mpeg::MpegFile; use lofty::{Accessor, AudioFile, ParseOptions}; #[test] -#[ignore] fn test_audio_properties_xing_header_cbr() { let f = get_file::("tests/taglib/data/lame_cbr.mp3"); @@ -25,7 +24,6 @@ fn test_audio_properties_xing_header_cbr() { } #[test] -#[ignore] fn test_audio_properties_xing_header_vbr() { let f = get_file::("tests/taglib/data/lame_vbr.mp3"); @@ -39,7 +37,6 @@ fn test_audio_properties_xing_header_vbr() { } #[test] -#[ignore] fn test_audio_properties_vbri_header() { let f = get_file::("tests/taglib/data/rare_frames.mp3"); @@ -53,7 +50,6 @@ fn test_audio_properties_vbri_header() { } #[test] -#[ignore] fn test_audio_properties_no_vbr_headers() { let f = get_file::("tests/taglib/data/bladeenc.mp3"); @@ -79,7 +75,6 @@ fn test_skip_invalid_frames_1() { } #[test] -#[ignore] fn test_skip_invalid_frames_2() { let f = get_file::("tests/taglib/data/invalid-frames2.mp3"); @@ -91,7 +86,6 @@ fn test_skip_invalid_frames_2() { } #[test] -#[ignore] fn test_skip_invalid_frames_3() { let f = get_file::("tests/taglib/data/invalid-frames3.mp3"); @@ -103,7 +97,6 @@ fn test_skip_invalid_frames_3() { } #[test] -#[ignore] fn test_version_2_duration_with_xing_header() { let f = get_file::("tests/taglib/data/mpeg2.mp3"); assert_eq!(f.properties().duration().as_secs(), 5387); // TODO: Off by 15 @@ -141,8 +134,9 @@ fn test_save_id3v24_wrong_param() { // Marker test, Lofty does not replicate the TagLib saving API } +// TODO: We don't yet support writing an ID3v23 tag (#62) #[test] -#[ignore] // TODO: We don't yet support writing an ID3v23 tag (#62) +#[ignore] fn test_save_id3v23() { let mut file = temp_file!("tests/taglib/data/xing.mp3"); @@ -292,8 +286,8 @@ fn test_empty_ape() { // Marker test, Lofty accepts empty strings as valid values } +// TODO: We can't find an ID3v2 tag after saving with garbage #[test] -#[ignore] // TODO: We can't find an ID3v2 tag after saving with garbage fn test_ignore_garbage() { let mut file = temp_file!("tests/taglib/data/garbage.mp3"); diff --git a/tests/taglib/test_ogaflac.rs b/tests/taglib/test_ogaflac.rs index fe38b486..58c2450d 100644 --- a/tests/taglib/test_ogaflac.rs +++ b/tests/taglib/test_ogaflac.rs @@ -7,37 +7,37 @@ use lofty::ogg::VorbisComments; use lofty::{Accessor, AudioFile, ParseOptions}; #[test] -#[ignore] // TODO: We don't support FLAC in OGA fn test_framing_bit() { - let mut file = temp_file!("tests/taglib/data/empty_flac.oga"); - - { - let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); - file.rewind().unwrap(); - - let mut vorbis_comments = VorbisComments::new(); - vorbis_comments.set_artist(String::from("The Artist")); - f.set_vorbis_comments(vorbis_comments); - f.save_to(&mut file).unwrap(); - } - file.rewind().unwrap(); - { - let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); - assert_eq!( - f.vorbis_comments().unwrap().artist().as_deref(), - Some("The Artist") - ); - - assert_eq!(file.seek(SeekFrom::End(0)).unwrap(), 9134); - } + todo!("We don't support FLAC in OGA") + // let mut file = temp_file!("tests/taglib/data/empty_flac.oga"); + // + // { + // let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + // file.rewind().unwrap(); + // + // let mut vorbis_comments = VorbisComments::new(); + // vorbis_comments.set_artist(String::from("The Artist")); + // f.set_vorbis_comments(vorbis_comments); + // f.save_to(&mut file).unwrap(); + // } + // file.rewind().unwrap(); + // { + // let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + // assert_eq!( + // f.vorbis_comments().unwrap().artist().as_deref(), + // Some("The Artist") + // ); + // + // assert_eq!(file.seek(SeekFrom::End(0)).unwrap(), 9134); + // } } #[test] -#[ignore] // TODO fn test_fuzzed_file() { - let mut file = temp_file!("tests/taglib/data/segfault.oga"); - let f = FlacFile::read_from(&mut file, ParseOptions::new()); - assert!(f.is_err()); + todo!("We don't support FLAC in OGA") + // let mut file = temp_file!("tests/taglib/data/segfault.oga"); + // let f = FlacFile::read_from(&mut file, ParseOptions::new()); + // assert!(f.is_err()); } #[test] diff --git a/tests/taglib/test_ogg.rs b/tests/taglib/test_ogg.rs index a9343308..88369ec3 100644 --- a/tests/taglib/test_ogg.rs +++ b/tests/taglib/test_ogg.rs @@ -87,8 +87,8 @@ fn test_audio_properties() { assert_eq!(f.properties().bitrate_min(), 0); } +// TODO: Need to look into this one, not sure why there's a difference in checksums #[test] -#[ignore] // TODO: Need to look into this one, not sure why there's a difference in checksums fn test_page_checksum() { let mut file = temp_file!("tests/taglib/data/empty.ogg"); diff --git a/tests/taglib/test_speex.rs b/tests/taglib/test_speex.rs index 10cc332f..7f9edb33 100644 --- a/tests/taglib/test_speex.rs +++ b/tests/taglib/test_speex.rs @@ -7,7 +7,6 @@ use lofty::ogg::{SpeexFile, VorbisComments}; use lofty::{Accessor, AudioFile, ParseOptions}; #[test] -#[ignore] fn test_audio_properties() { let f = get_file::("tests/taglib/data/empty.spx"); @@ -21,8 +20,8 @@ fn test_audio_properties() { assert_eq!(f.properties().sample_rate(), 44100); } +// TODO: This test doesn't work, it's very specific with file/packet sizes. Have to determine whether or not to even keep this one. #[test] -#[ignore] // TODO: This test doesn't work, it's very specific with file/packet sizes. Have to determine whether or not to even keep this one. fn test_split_packets() { let mut file = temp_file!("tests/taglib/data/empty.spx"); diff --git a/tests/taglib/test_wav.rs b/tests/taglib/test_wav.rs index a2342492..d662c41a 100644 --- a/tests/taglib/test_wav.rs +++ b/tests/taglib/test_wav.rs @@ -106,28 +106,28 @@ fn test_id3v2_tag() { } #[test] -#[ignore] // TODO: Support writing ID3v2.3 fn test_save_id3v23() { - let mut file = temp_file!("tests/taglib/data/empty.wav"); - - let xxx = "X".repeat(254); - { - let mut f = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); - assert!(f.id3v2().is_none()); - - f.id3v2_mut().unwrap().set_title(xxx.clone()); - f.id3v2_mut().unwrap().set_artist(String::from("Artist A")); - - // f.save(RIFF::WAV::File::AllTags, File::StripOthers, ID3v2::v3); - // assert!(f.id3v2().is_some()); - } - { - let f2 = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); - let tag = f2.id3v2().unwrap(); - assert_eq!(tag.original_version(), Id3v2Version::V3); - assert_eq!(tag.artist().as_deref(), Some("Artist A")); - assert_eq!(tag.title().as_deref(), Some(&*xxx)); - } + todo!("Support writing ID3v2.3") + // let mut file = temp_file!("tests/taglib/data/empty.wav"); + // + // let xxx = "X".repeat(254); + // { + // let mut f = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); + // assert!(f.id3v2().is_none()); + // + // f.id3v2_mut().unwrap().set_title(xxx.clone()); + // f.id3v2_mut().unwrap().set_artist(String::from("Artist A")); + // + // // f.save(RIFF::WAV::File::AllTags, File::StripOthers, ID3v2::v3); + // // assert!(f.id3v2().is_some()); + // } + // { + // let f2 = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); + // let tag = f2.id3v2().unwrap(); + // assert_eq!(tag.original_version(), Id3v2Version::V3); + // assert_eq!(tag.artist().as_deref(), Some("Artist A")); + // assert_eq!(tag.title().as_deref(), Some(&*xxx)); + // } } #[test] @@ -275,49 +275,49 @@ fn test_fuzzed_file2() { } #[test] -#[ignore] // TODO: Doesn't pass, need to check what TagLib is doing here fn test_file_with_garbage_appended() { - let mut file = temp_file!("tests/taglib/data/empty.wav"); - let contents_before_modification; - { - file.seek(SeekFrom::End(0)).unwrap(); - - let garbage = b"12345678"; - file.write_all(garbage).unwrap(); - file.rewind().unwrap(); - - let mut file_contents = Vec::new(); - file.read_to_end(&mut file_contents).unwrap(); - - contents_before_modification = file_contents; - } - file.rewind().unwrap(); - { - let mut f = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); - file.rewind().unwrap(); - - let mut id3v2 = Id3v2Tag::default(); - id3v2.set_title(String::from("ID3v2 Title")); - f.set_id3v2(id3v2); - - let mut riff_info = RIFFInfoList::default(); - riff_info.set_title(String::from("INFO Title")); - f.set_riff_info(riff_info); - - f.save_to(&mut file).unwrap(); - } - file.rewind().unwrap(); - { - TagType::Id3v2.remove_from(&mut file).unwrap(); - file.rewind().unwrap(); - TagType::RiffInfo.remove_from(&mut file).unwrap(); - } - file.rewind().unwrap(); - { - let mut contents_after_modification = Vec::new(); - file.read_to_end(&mut contents_after_modification).unwrap(); - assert_eq!(contents_before_modification, contents_after_modification); - } + todo!("Doesn't pass, need to check what TagLib is doing here") + // let mut file = temp_file!("tests/taglib/data/empty.wav"); + // let contents_before_modification; + // { + // file.seek(SeekFrom::End(0)).unwrap(); + // + // let garbage = b"12345678"; + // file.write_all(garbage).unwrap(); + // file.rewind().unwrap(); + // + // let mut file_contents = Vec::new(); + // file.read_to_end(&mut file_contents).unwrap(); + // + // contents_before_modification = file_contents; + // } + // file.rewind().unwrap(); + // { + // let mut f = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); + // file.rewind().unwrap(); + // + // let mut id3v2 = Id3v2Tag::default(); + // id3v2.set_title(String::from("ID3v2 Title")); + // f.set_id3v2(id3v2); + // + // let mut riff_info = RIFFInfoList::default(); + // riff_info.set_title(String::from("INFO Title")); + // f.set_riff_info(riff_info); + // + // f.save_to(&mut file).unwrap(); + // } + // file.rewind().unwrap(); + // { + // TagType::Id3v2.remove_from(&mut file).unwrap(); + // file.rewind().unwrap(); + // TagType::RiffInfo.remove_from(&mut file).unwrap(); + // } + // file.rewind().unwrap(); + // { + // let mut contents_after_modification = Vec::new(); + // file.read_to_end(&mut contents_after_modification).unwrap(); + // assert_eq!(contents_before_modification, contents_after_modification); + // } } #[test] From 9ed604fcfacc10ddb4b043e0a5a9cd37b41a6394 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 27 Jul 2023 14:00:00 -0400 Subject: [PATCH 44/75] Tests: Ignore testWavPack::testMultiChannelProperties --- tests/taglib/data/four_channels.wv | Bin 53520 -> 0 bytes tests/taglib/test_wavpack.rs | 15 +++++---------- 2 files changed, 5 insertions(+), 10 deletions(-) delete mode 100644 tests/taglib/data/four_channels.wv diff --git a/tests/taglib/data/four_channels.wv b/tests/taglib/data/four_channels.wv deleted file mode 100644 index de682f242fdb7eb8792bc3d33b10361a3b6c731c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53520 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Ҵ8( zn}|J)l0lWtvqN%Nl7u>vBr9(NcTb4iEJ#6{exOCa^^3v?T?V^{nu4q5$2NxF3r&u* zrDRVMPPoZ5O9Zz(vyBIM<}k+f^)8sg31jEaZfNOoq#}q3EcSif(I|;%OUyp5xwa;l zqMq!9?xB7Lf2-ac5@t6~QT5I-;0|dm!y$gW&q` z<7ahPq&BbpWWDRxU=b|v%mhsLl}%P=-ZE3(D)eBqNWSW_uHtQ37cdIflr zggON^VxL6-B=aP%jUAepp}EcK_nY0s^8wlE;-lOHZ+;X91tTp&>(or-)rxivY~y+B z1M|K2A?*wK1YTkHRDO6#%-W;|1Yk45EP+JzRL$E8X>@!5Bbr44=qej2CDCEXp-sP`movVh8@ zBCl$gMn1u%=;?Ik!XQgd6V;bigYVDgA6YjwK!1xwUSFep;5;T~FQC-iDK4Lj=$;Pj z86>}l2iHPY!fDV|y%Un@o}|tTu?8l9cWZFw2OMNsqp*K3W(;#Qyng)t?DKNyJ>{}} z{1G&9tqR-+Z@aO?&{nyqm3G9uleHdzcGx;atm*7=H-eF2Gz70CC^?679(LUJ*LU@itjQ#JbCqObr}NdbP{T8Z4U zEcS$>vE?QsSqXu5Ehd?1r;IvpSSDd2-69)e�>pUG+-CyH%x(PN!u9KA5nzmBeVI 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 Z3_louwluOe$j@hI_-7#bBkRHP{{U-F$SnW> diff --git a/tests/taglib/test_wavpack.rs b/tests/taglib/test_wavpack.rs index 288a364e..e3ee5877 100644 --- a/tests/taglib/test_wavpack.rs +++ b/tests/taglib/test_wavpack.rs @@ -23,17 +23,12 @@ fn test_no_length_properties() { } #[test] +#[ignore] fn test_multi_channel_properties() { - let f = get_file::("tests/taglib/data/four_channels.wv"); - assert_eq!(f.properties().duration().as_secs(), 3); - assert_eq!(f.properties().duration().as_millis(), 3833); - assert_eq!(f.properties().audio_bitrate(), 112); - assert_eq!(f.properties().channels(), 4); - assert_eq!(f.properties().bit_depth(), 16); - assert_eq!(f.properties().is_lossless(), false); - assert_eq!(f.properties().sample_rate(), 44100); - // TODO: CPPUNIT_ASSERT_EQUAL(169031U, f.audioProperties()->sampleFrames()); - assert_eq!(f.properties().version(), 1031); + // Marker test, this is not a valid file and TagLib does not handle it properly. + // + // A multichannel file should make use of the multichannel metadata sub block, which + // this file does not. Even FFMpeg thinks this is a mono file. } #[test] From 9b0fcf0e242b759cfd9e87877369e4bd59f660cf Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Tue, 17 Oct 2023 17:53:44 -0400 Subject: [PATCH 45/75] Tests: Update TagLib tests for latest changes --- tests/taglib/test_flacpicture.rs | 6 +++--- tests/taglib/test_id3v2.rs | 31 ++++++++++++++++++++++++++----- tests/taglib/test_wav.rs | 4 ++-- tests/taglib/test_wavpack.rs | 4 +++- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/tests/taglib/test_flacpicture.rs b/tests/taglib/test_flacpicture.rs index 16826738..03e49f0c 100644 --- a/tests/taglib/test_flacpicture.rs +++ b/tests/taglib/test_flacpicture.rs @@ -1,4 +1,4 @@ -use lofty::{MimeType, Picture, PictureType}; +use lofty::{MimeType, ParsingMode, Picture, PictureType}; const DATA: &[u8] = &[ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x09, 0x69, 0x6D, 0x61, 0x67, 0x65, 0x2F, 0x70, 0x6E, @@ -18,7 +18,7 @@ const DATA: &[u8] = &[ #[test] fn test_parse() { - let (picture, info) = Picture::from_flac_bytes(DATA, false).unwrap(); + let (picture, info) = Picture::from_flac_bytes(DATA, false, ParsingMode::Strict).unwrap(); assert_eq!(picture.pic_type(), PictureType::CoverFront); assert_eq!(info.width, 1); @@ -32,6 +32,6 @@ fn test_parse() { #[test] fn test_pass_through() { - let (picture, info) = Picture::from_flac_bytes(DATA, false).unwrap(); + let (picture, info) = Picture::from_flac_bytes(DATA, false, ParsingMode::Strict).unwrap(); assert_eq!(DATA, picture.as_flac_bytes(info, false).as_slice()); } diff --git a/tests/taglib/test_id3v2.rs b/tests/taglib/test_id3v2.rs index f4ba659f..3192d0fe 100644 --- a/tests/taglib/test_id3v2.rs +++ b/tests/taglib/test_id3v2.rs @@ -258,7 +258,11 @@ fn test_popm_from_file() { file.rewind().unwrap(); { let bar = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); - let popm_frame = bar.id3v2().unwrap().get("POPM").unwrap(); + let popm_frame = bar + .id3v2() + .unwrap() + .get(&FrameId::Valid(Cow::Borrowed("POPM"))) + .unwrap(); let popularimeter = match popm_frame.content() { FrameValue::Popularimeter(popm) => popm, _ => unreachable!(), @@ -738,7 +742,10 @@ fn test_itunes_24_frame_size() { .unwrap() .contains(&FrameId::Valid(Cow::from("TIT2")))); assert_eq!( - f.id3v2().unwrap().get_text("TIT2").unwrap(), + f.id3v2() + .unwrap() + .get_text(&FrameId::Valid(Cow::Borrowed("TIT2"))) + .unwrap(), "Sunshine Superman" ); } @@ -818,7 +825,13 @@ fn test_update_full_date22() { let mut file = temp_file!("tests/taglib/data/id3v22-tda.mp3"); let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); assert!(f.id3v2().is_some()); - assert_eq!(f.id3v2().unwrap().get_text("TDRC").unwrap(), "2010-04-03"); + assert_eq!( + f.id3v2() + .unwrap() + .get_text(&FrameId::Valid(Cow::Borrowed("TDRC"))) + .unwrap(), + "2010-04-03" + ); } #[test] @@ -835,7 +848,11 @@ fn test_compressed_frame_with_broken_length() { .unwrap() .contains(&FrameId::Valid(Cow::from("APIC")))); - let frame = f.id3v2().unwrap().get("APIC").unwrap(); + let frame = f + .id3v2() + .unwrap() + .get(&FrameId::Valid(Cow::Borrowed("APIC"))) + .unwrap(); let picture = match frame.content() { FrameValue::Picture(AttachedPictureFrame { picture, .. }) => picture, _ => unreachable!(), @@ -856,7 +873,11 @@ fn test_w000() { .id3v2() .unwrap() .contains(&FrameId::Valid(Cow::from("W000")))); - let frame = f.id3v2().unwrap().get("W000").unwrap(); + let frame = f + .id3v2() + .unwrap() + .get(&FrameId::Valid(Cow::Borrowed("W000"))) + .unwrap(); let url_frame = match frame.content() { FrameValue::Url(url_frame) => url_frame, _ => unreachable!(), diff --git a/tests/taglib/test_wav.rs b/tests/taglib/test_wav.rs index d662c41a..a847822a 100644 --- a/tests/taglib/test_wav.rs +++ b/tests/taglib/test_wav.rs @@ -1,9 +1,9 @@ use crate::temp_file; use crate::util::get_file; -use lofty::id3::v2::{Id3v2Tag, Id3v2Version}; +use lofty::id3::v2::Id3v2Tag; use lofty::iff::wav::{RIFFInfoList, WavFile, WavFormat}; use lofty::{Accessor, AudioFile, ParseOptions, TagType}; -use std::io::{Cursor, Read, Seek, SeekFrom, Write}; +use std::io::{Cursor, Read, Seek, SeekFrom}; #[test] fn test_pcm_properties() { diff --git a/tests/taglib/test_wavpack.rs b/tests/taglib/test_wavpack.rs index e3ee5877..aab50175 100644 --- a/tests/taglib/test_wavpack.rs +++ b/tests/taglib/test_wavpack.rs @@ -1,5 +1,6 @@ use crate::temp_file; use crate::util::get_file; +use std::fs::File; use std::io::Seek; @@ -75,7 +76,8 @@ fn test_tagged_properties() { #[test] fn test_fuzzed_file() { - let _f = get_file::("tests/taglib/data/infloop.wv"); + let mut f = File::open("tests/taglib/data/infloop.wv").unwrap(); + assert!(WavPackFile::read_from(&mut f, ParseOptions::new()).is_err()); } #[test] From ac926568d52890aa4640bf7279f9cded115fa31a Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 26 Oct 2023 13:22:19 -0400 Subject: [PATCH 46/75] Tests: Ignore TagLib ID3v2.3 conversion tests for now --- tests/taglib/test_aiff.rs | 1 + tests/taglib/test_id3v2.rs | 3 +++ 2 files changed, 4 insertions(+) diff --git a/tests/taglib/test_aiff.rs b/tests/taglib/test_aiff.rs index c1d15c00..d30a3096 100644 --- a/tests/taglib/test_aiff.rs +++ b/tests/taglib/test_aiff.rs @@ -93,6 +93,7 @@ fn test_save_id3v2() { } #[test] +#[ignore] fn test_save_id3v23() { todo!("Support writing ID3v2.3 tags") } diff --git a/tests/taglib/test_id3v2.rs b/tests/taglib/test_id3v2.rs index 3192d0fe..7c1bb97f 100644 --- a/tests/taglib/test_id3v2.rs +++ b/tests/taglib/test_id3v2.rs @@ -30,11 +30,13 @@ fn test_unsynch_decode() { } #[test] +#[ignore] fn test_downgrade_utf8_for_id3v23_1() { todo!("We don't support downgrading 2.4 tags to 2.3") } #[test] +#[ignore] fn test_downgrade_utf8_for_id3v23_2() { todo!("We don't support downgrading 2.4 tags to 2.3") } @@ -835,6 +837,7 @@ fn test_update_full_date22() { } #[test] +#[ignore] fn test_downgrade_to_23() { todo!("We don't support downgrading 2.4 tags to 2.3") } From b6805315eb6b8f1e9dee683390b28cbca6b2f0ac Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sun, 3 Dec 2023 14:39:53 -0500 Subject: [PATCH 47/75] Tests: Simply ignore tests with unimplemented/uncertain behavior --- tests/taglib/test_apetag.rs | 1 + tests/taglib/test_fileref.rs | 1 + tests/taglib/test_flac.rs | 3 ++ tests/taglib/test_id3v2.rs | 53 ++++++++++++++++++------------------ tests/taglib/test_mp4.rs | 5 ++-- tests/taglib/test_ogaflac.rs | 9 +++--- 6 files changed, 38 insertions(+), 34 deletions(-) diff --git a/tests/taglib/test_apetag.rs b/tests/taglib/test_apetag.rs index bc611624..2bc591ab 100644 --- a/tests/taglib/test_apetag.rs +++ b/tests/taglib/test_apetag.rs @@ -91,6 +91,7 @@ fn test_text_binary() { // TODO: Does not work! We fall for this collision. #[test] +#[ignore] fn test_id3v1_collision() { let mut file = temp_file!("tests/taglib/data/no-tags.mpc"); { diff --git a/tests/taglib/test_fileref.rs b/tests/taglib/test_fileref.rs index 174c77aa..4eaff6d2 100644 --- a/tests/taglib/test_fileref.rs +++ b/tests/taglib/test_fileref.rs @@ -216,6 +216,7 @@ fn test_default_file_extensions() { // TODO: We need to check resolvers *first* and then resort to our default implementations #[test] +#[ignore] fn test_file_resolver() { { let file = lofty::read_from_path("tests/taglib/data/xing.mp3").unwrap(); diff --git a/tests/taglib/test_flac.rs b/tests/taglib/test_flac.rs index 6dd9bcf0..06fdce6f 100644 --- a/tests/taglib/test_flac.rs +++ b/tests/taglib/test_flac.rs @@ -221,6 +221,7 @@ fn test_repeated_save_2() { // TODO: We don't make use of padding blocks yet #[test] +#[ignore] fn test_repeated_save_3() { let mut file = temp_file!("tests/taglib/data/no-tags.flac"); @@ -424,6 +425,7 @@ fn test_zero_sized_padding_2() { // TODO: We don't make use of padding blocks yet #[test] +#[ignore] fn test_shrink_padding() { let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); { @@ -480,6 +482,7 @@ fn test_empty_id3v2() { // TODO: TagLib doesn't fully remove Vorbis Comments when stripping. It will preserve the vendor string. Should we do the same? #[test] +#[ignore] fn test_strip_tags() { // NOTE: In the TagLib test suite, this also tests ID3v1 and ID3v2. That is not replicated here. diff --git a/tests/taglib/test_id3v2.rs b/tests/taglib/test_id3v2.rs index 7c1bb97f..297dbef9 100644 --- a/tests/taglib/test_id3v2.rs +++ b/tests/taglib/test_id3v2.rs @@ -29,17 +29,15 @@ fn test_unsynch_decode() { ); } +// TODO: Support downgrading to ID3v2.3 (#62) #[test] #[ignore] -fn test_downgrade_utf8_for_id3v23_1() { - todo!("We don't support downgrading 2.4 tags to 2.3") -} +fn test_downgrade_utf8_for_id3v23_1() {} +// TODO: Support downgrading to ID3v2.3 (#62) #[test] #[ignore] -fn test_downgrade_utf8_for_id3v23_2() { - todo!("We don't support downgrading 2.4 tags to 2.3") -} +fn test_downgrade_utf8_for_id3v23_2() {} #[test] fn test_utf16be_delimiter() { @@ -735,6 +733,7 @@ fn test_render_user_text_identification_frame() { // TODO: iTunes, being the great application it is writes unsynchronized integers for sizes. There's no *great* way to detect this. #[test] +#[ignore] fn test_itunes_24_frame_size() { let mut file = temp_file!("tests/taglib/data/005411.id3"); let f = MpegFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); @@ -788,10 +787,10 @@ fn test_save_utf16_comment() { } } +// TODO: Support downgrading to ID3v2.3 (#62) #[test] -fn test_update_genre_23_1() { - todo!("We don't support downgrading to 2.3 tags yet") -} +#[ignore] +fn test_update_genre_23_1() {} #[test] #[ignore] @@ -823,6 +822,7 @@ fn test_update_date22() { // TODO: Determine if this is even worth doing. It is just combining TYE+TDA when upgrading ID3v2.2 to 2.4 #[test] +#[ignore] fn test_update_full_date22() { let mut file = temp_file!("tests/taglib/data/id3v22-tda.mp3"); let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); @@ -836,11 +836,10 @@ fn test_update_full_date22() { ); } +// TODO: Support downgrading to ID3v2.3 (#62) #[test] #[ignore] -fn test_downgrade_to_23() { - todo!("We don't support downgrading 2.4 tags to 2.3") -} +fn test_downgrade_to_23() {} #[test] fn test_compressed_frame_with_broken_length() { @@ -965,25 +964,25 @@ fn test_save_and_strip_id3v1_should_not_add_frame_from_id3v1_to_id3v2() { assert!(f.id3v2().is_none()); } +// TODO: Support CHAP frames (#189) #[test] -fn test_parse_chapter_frame() { - todo!("We don't support CHAP frames yet") -} +#[ignore] +fn test_parse_chapter_frame() {} +// TODO: Support CHAP frames (#189) #[test] -fn test_render_chapter_frame() { - todo!("We don't support CHAP frames yet") -} +#[ignore] +fn test_render_chapter_frame() {} +// TODO: Support CTOC frames (#189) #[test] -fn test_parse_table_of_contents_frame() { - todo!("We don't support CTOC frames yet") -} +#[ignore] +fn test_parse_table_of_contents_frame() {} +// TODO: Support CTOC frames (#189) #[test] -fn test_render_table_of_contents_frame() { - todo!("We don't support CTOC frames yet") -} +#[ignore] +fn test_render_table_of_contents_frame() {} #[test] #[ignore] @@ -997,7 +996,7 @@ fn test_duplicate_tags() { // Marker test, Lofty will combine duplicated tags } +// TODO: Support CTOC frames (#189) #[test] -fn test_parse_toc_frame_with_many_children() { - todo!("We don't support CTOC frames yet") -} +#[ignore] +fn test_parse_toc_frame_with_many_children() {} diff --git a/tests/taglib/test_mp4.rs b/tests/taglib/test_mp4.rs index bab6d8ea..b6c166d3 100644 --- a/tests/taglib/test_mp4.rs +++ b/tests/taglib/test_mp4.rs @@ -234,10 +234,9 @@ fn test_save_existing_when_ilst_is_last() { } #[test] +#[ignore] fn test_64bit_atom() { - todo!( - "Maybe? This just checks the moov atom's length. We don't retain any atoms we don't need." - ) + // Marker test, this just checks the moov atom's length. We don't retain any atoms we don't need. } #[test] diff --git a/tests/taglib/test_ogaflac.rs b/tests/taglib/test_ogaflac.rs index 58c2450d..65c6f800 100644 --- a/tests/taglib/test_ogaflac.rs +++ b/tests/taglib/test_ogaflac.rs @@ -32,12 +32,13 @@ fn test_framing_bit() { // } } +// TODO: We don't support FLAC in OGA (#172) #[test] +#[ignore] fn test_fuzzed_file() { - todo!("We don't support FLAC in OGA") - // let mut file = temp_file!("tests/taglib/data/segfault.oga"); - // let f = FlacFile::read_from(&mut file, ParseOptions::new()); - // assert!(f.is_err()); + let mut file = temp_file!("tests/taglib/data/segfault.oga"); + let f = FlacFile::read_from(&mut file, ParseOptions::new()); + assert!(f.is_err()); } #[test] From 6a5e54a126402c720026ab95ecbfb2d2e27d33d1 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sun, 3 Dec 2023 14:55:34 -0500 Subject: [PATCH 48/75] Tests: Implement TagLib ID3v2 UTF16 delimiter tests --- tests/taglib/test_id3v2.rs | 64 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/tests/taglib/test_id3v2.rs b/tests/taglib/test_id3v2.rs index 297dbef9..74c13902 100644 --- a/tests/taglib/test_id3v2.rs +++ b/tests/taglib/test_id3v2.rs @@ -9,7 +9,8 @@ use lofty::id3::v2::{ EventTimingCodesFrame, EventType, ExtendedTextFrame, ExtendedUrlFrame, Frame, FrameFlags, FrameId, FrameValue, GeneralEncapsulatedObject, Id3v2Tag, Id3v2Version, OwnershipFrame, Popularimeter, PrivateFrame, RelativeVolumeAdjustmentFrame, SyncTextContentType, - SynchronizedText, TimestampFormat, UniqueFileIdentifierFrame, UrlLinkFrame, + SynchronizedText, TextInformationFrame, TimestampFormat, UniqueFileIdentifierFrame, + UrlLinkFrame, }; use lofty::mpeg::MpegFile; use lofty::{ @@ -41,12 +42,69 @@ fn test_downgrade_utf8_for_id3v23_2() {} #[test] fn test_utf16be_delimiter() { - todo!("Need to think of a nice way to handle multiple UTF-16 values separated by null") + let mut f = TextInformationFrame { + encoding: TextEncoding::UTF16BE, + value: String::from("Foo\0Bar"), + }; + + let data = f.as_bytes(); + + let no_bom_be_data = b"\x02\ + \0F\0o\0o\0\0\ + \0B\0a\0r"; + + assert_eq!(data, no_bom_be_data); + f = TextInformationFrame::parse(&mut &data[..], Id3v2Version::V4) + .unwrap() + .unwrap(); + assert_eq!(f.value, "Foo\0Bar"); } #[test] fn test_utf16_delimiter() { - todo!("Need to think of a nice way to handle multiple UTF-16 values separated by null") + let mut f = TextInformationFrame { + encoding: TextEncoding::UTF16, + value: String::from("Foo\0Bar"), + }; + + let data = f.as_bytes(); + + // TODO: TagLib writes a BOM to every string, making the output identical to `mutli_bom_le_data`, + // rather than `single_bom_le_data` in Lofty's case. Not sure if we should be writing the BOM + // to every string? + let single_bom_le_data = b"\x01\xff\xfe\ + F\0o\0o\0\0\0\ + B\0a\0r\0"; + + assert_eq!(data, single_bom_le_data); + f = TextInformationFrame::parse(&mut &data[..], Id3v2Version::V4) + .unwrap() + .unwrap(); + assert_eq!(f.value, "Foo\0Bar"); + + let multi_bom_le_data = b"\x01\xff\xfe\ + F\0o\0o\0\0\0\xff\xfe\ + B\0a\0r\0"; + f = TextInformationFrame::parse(&mut &multi_bom_le_data[..], Id3v2Version::V4) + .unwrap() + .unwrap(); + assert_eq!(f.value, "Foo\0Bar"); + + let multi_bom_be_data = b"\x01\xfe\xff\ + \0F\0o\0o\0\0\xfe\xff\ + \0B\0a\0r"; + f = TextInformationFrame::parse(&mut &multi_bom_be_data[..], Id3v2Version::V4) + .unwrap() + .unwrap(); + assert_eq!(f.value, "Foo\0Bar"); + + let single_bom_be_data = b"\x01\xfe\xff\ + \0F\0o\0o\0\0\ + \0B\0a\0r"; + f = TextInformationFrame::parse(&mut &single_bom_be_data[..], Id3v2Version::V4) + .unwrap() + .unwrap(); + assert_eq!(f.value, "Foo\0Bar"); } #[test] From d213550bbb20d806d944c13867ea0f5ceb8e7e2c Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sun, 3 Dec 2023 15:06:11 -0500 Subject: [PATCH 49/75] Tests: Implement TagLib TestID3v2::testUpdateGenre24 --- tests/taglib/test_id3v2.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/taglib/test_id3v2.rs b/tests/taglib/test_id3v2.rs index 74c13902..4d5d16f7 100644 --- a/tests/taglib/test_id3v2.rs +++ b/tests/taglib/test_id3v2.rs @@ -864,10 +864,18 @@ fn test_update_genre23_3() { #[test] fn test_update_genre_24() { - todo!( - "We currently just return the genre string as it is in the tag, need to think about \ - whether or not to convert numerical strings" + let frame_value = TextInformationFrame::parse( + &mut &b"\x00\ + 14\0Eurodisco"[..], + Id3v2Version::V4, ) + .unwrap() + .unwrap(); + + let mut tag = Id3v2Tag::new(); + tag.insert(Frame::new("TCON", frame_value, FrameFlags::default()).unwrap()); + + assert_eq!(tag.genre().as_deref(), Some("R&B / Eurodisco")); } #[test] From 35f02ab8ce41028369704c190298921106edc57a Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sun, 3 Dec 2023 15:21:38 -0500 Subject: [PATCH 50/75] Tests: Ignore more TagLib tests with unimplemented features --- tests/taglib/test_ogaflac.rs | 45 ++++++++++++++++++------------------ tests/taglib/test_ogg.rs | 1 + tests/taglib/test_wav.rs | 3 ++- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/tests/taglib/test_ogaflac.rs b/tests/taglib/test_ogaflac.rs index 65c6f800..fd404bc3 100644 --- a/tests/taglib/test_ogaflac.rs +++ b/tests/taglib/test_ogaflac.rs @@ -6,30 +6,31 @@ use lofty::flac::FlacFile; use lofty::ogg::VorbisComments; use lofty::{Accessor, AudioFile, ParseOptions}; +// TODO: We don't support FLAC in OGA (#172) #[test] +#[ignore] fn test_framing_bit() { - todo!("We don't support FLAC in OGA") - // let mut file = temp_file!("tests/taglib/data/empty_flac.oga"); - // - // { - // let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); - // file.rewind().unwrap(); - // - // let mut vorbis_comments = VorbisComments::new(); - // vorbis_comments.set_artist(String::from("The Artist")); - // f.set_vorbis_comments(vorbis_comments); - // f.save_to(&mut file).unwrap(); - // } - // file.rewind().unwrap(); - // { - // let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); - // assert_eq!( - // f.vorbis_comments().unwrap().artist().as_deref(), - // Some("The Artist") - // ); - // - // assert_eq!(file.seek(SeekFrom::End(0)).unwrap(), 9134); - // } + let mut file = temp_file!("tests/taglib/data/empty_flac.oga"); + + { + let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut vorbis_comments = VorbisComments::new(); + vorbis_comments.set_artist(String::from("The Artist")); + f.set_vorbis_comments(vorbis_comments); + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert_eq!( + f.vorbis_comments().unwrap().artist().as_deref(), + Some("The Artist") + ); + + assert_eq!(file.seek(SeekFrom::End(0)).unwrap(), 9134); + } } // TODO: We don't support FLAC in OGA (#172) diff --git a/tests/taglib/test_ogg.rs b/tests/taglib/test_ogg.rs index 88369ec3..5cd4d62c 100644 --- a/tests/taglib/test_ogg.rs +++ b/tests/taglib/test_ogg.rs @@ -89,6 +89,7 @@ fn test_audio_properties() { // TODO: Need to look into this one, not sure why there's a difference in checksums #[test] +#[ignore] fn test_page_checksum() { let mut file = temp_file!("tests/taglib/data/empty.ogg"); diff --git a/tests/taglib/test_wav.rs b/tests/taglib/test_wav.rs index a847822a..361ae86e 100644 --- a/tests/taglib/test_wav.rs +++ b/tests/taglib/test_wav.rs @@ -105,9 +105,10 @@ fn test_id3v2_tag() { } } +// TODO: Support downgrading to ID3v2.3 (#62) #[test] +#[ignore] fn test_save_id3v23() { - todo!("Support writing ID3v2.3") // let mut file = temp_file!("tests/taglib/data/empty.wav"); // // let xxx = "X".repeat(254); From 896d9e8ea49a5986ee9cd70a6a68cefb33c398c8 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sun, 3 Dec 2023 16:28:03 -0500 Subject: [PATCH 51/75] Tests: Use `Mp4Properties::is_drm_properties()` in TagLib tests --- tests/taglib/test_mp4.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/taglib/test_mp4.rs b/tests/taglib/test_mp4.rs index b6c166d3..3c25a4e3 100644 --- a/tests/taglib/test_mp4.rs +++ b/tests/taglib/test_mp4.rs @@ -16,8 +16,7 @@ fn test_properties_aac() { assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().sample_rate(), 44100); assert_eq!(f.properties().bit_depth(), Some(16)); - // TODO: Check for encryption - // assert_eq!(f.properties().encrypted, false); + assert!(!f.properties().is_drm_protected()); assert_eq!(f.properties().codec(), &Mp4Codec::AAC); } @@ -42,7 +41,7 @@ fn test_properties_aac_without_bitrate() { assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().sample_rate(), 44100); assert_eq!(f.properties().bit_depth(), Some(16)); - // assert_eq!(f.properties().encrypted, false); + assert!(!f.properties().is_drm_protected()); assert_eq!(f.properties().codec(), &Mp4Codec::AAC); } @@ -55,7 +54,7 @@ fn test_properties_alac() { assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().sample_rate(), 44100); assert_eq!(f.properties().bit_depth(), Some(16)); - // assert_eq!(f.properties().encrypted, false); + assert!(!f.properties().is_drm_protected()); assert_eq!(f.properties().codec(), &Mp4Codec::ALAC); } @@ -80,7 +79,7 @@ fn test_properties_alac_without_bitrate() { assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().sample_rate(), 44100); assert_eq!(f.properties().bit_depth(), Some(16)); - // assert_eq!(f.properties().encrypted, false); + assert_eq!(f.properties().is_drm_protected(), false); assert_eq!(f.properties().codec(), &Mp4Codec::ALAC); } @@ -93,7 +92,7 @@ fn test_properties_m4v() { assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().sample_rate(), 44100); assert_eq!(f.properties().bit_depth(), Some(16)); - // assert_eq!(f.properties().encrypted, false); + assert!(!f.properties().is_drm_protected()); assert_eq!(f.properties().codec(), &Mp4Codec::AAC); } From c0e0cf0717832a5ef2e54a5d940ec773cddcfd36 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 14 Dec 2023 12:07:33 -0500 Subject: [PATCH 52/75] Tests: Comment out TagLib TestID3v2::testBrokenFrame1 Unsure if this is something we even want to support. --- tests/taglib/test_id3v2.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/taglib/test_id3v2.rs b/tests/taglib/test_id3v2.rs index 4d5d16f7..c7341147 100644 --- a/tests/taglib/test_id3v2.rs +++ b/tests/taglib/test_id3v2.rs @@ -108,14 +108,17 @@ fn test_utf16_delimiter() { } #[test] +#[ignore] fn test_broken_frame1() { - let mut file = temp_file!("tests/taglib/data/broken-tenc.id3"); - let f = MpegFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); - - assert!(f - .id3v2() - .unwrap() - .contains(&FrameId::Valid(Cow::from("TENC")))); + // TODO: Determine if it is worth supporting unsychronized frame sizes in ID3v2.4 + // This is apparently an issue iTunes had at some point in the past. + // let mut file = temp_file!("tests/taglib/data/broken-tenc.id3"); + // let f = MpegFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); + // + // assert!(f + // .id3v2() + // .unwrap() + // .contains(&FrameId::Valid(Cow::from("TENC")))); } #[test] From 64fefd2c436add37adba0ad6c1338a4622b0036f Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sun, 31 Dec 2023 13:49:48 -0500 Subject: [PATCH 53/75] Tests: Update for `MimeType` changes --- tests/taglib/test_flac.rs | 16 ++++++++-------- tests/taglib/test_flacpicture.rs | 2 +- tests/taglib/test_id3v2.rs | 13 ++++++++----- tests/taglib/test_mp4.rs | 20 ++++++++++---------- tests/taglib/test_xiphcomment.rs | 4 ++-- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/tests/taglib/test_flac.rs b/tests/taglib/test_flac.rs index 06fdce6f..cdccbf12 100644 --- a/tests/taglib/test_flac.rs +++ b/tests/taglib/test_flac.rs @@ -40,7 +40,7 @@ fn test_read_picture() { assert_eq!(info.height, 1); assert_eq!(info.color_depth, 24); assert_eq!(info.num_colors, 0); - assert_eq!(pic.mime_type(), &MimeType::Png); + assert_eq!(pic.mime_type(), Some(&MimeType::Png)); assert_eq!(pic.description(), Some("A pixel.")); assert_eq!(pic.data().len(), 150); } @@ -57,7 +57,7 @@ fn test_add_picture() { let new_pic = Picture::new_unchecked( PictureType::CoverBack, - MimeType::Jpeg, + Some(MimeType::Jpeg), Some(String::from("new image")), Vec::from("JPEG data"), ); @@ -85,7 +85,7 @@ fn test_add_picture() { assert_eq!(info.height, 1); assert_eq!(info.color_depth, 24); assert_eq!(info.num_colors, 0); - assert_eq!(pic.mime_type(), &MimeType::Png); + assert_eq!(pic.mime_type(), Some(&MimeType::Png)); assert_eq!(pic.description(), Some("A pixel.")); assert_eq!(pic.data().len(), 150); @@ -95,7 +95,7 @@ fn test_add_picture() { assert_eq!(info.height, 6); assert_eq!(info.color_depth, 16); assert_eq!(info.num_colors, 7); - assert_eq!(pic.mime_type(), &MimeType::Jpeg); + assert_eq!(pic.mime_type(), Some(&MimeType::Jpeg)); assert_eq!(pic.description(), Some("new image")); assert_eq!(pic.data(), b"JPEG data"); } @@ -113,7 +113,7 @@ fn test_replace_picture() { let new_pic = Picture::new_unchecked( PictureType::CoverBack, - MimeType::Jpeg, + Some(MimeType::Jpeg), Some(String::from("new image")), Vec::from("JPEG data"), ); @@ -142,7 +142,7 @@ fn test_replace_picture() { assert_eq!(info.height, 6); assert_eq!(info.color_depth, 16); assert_eq!(info.num_colors, 7); - assert_eq!(pic.mime_type(), &MimeType::Jpeg); + assert_eq!(pic.mime_type(), Some(&MimeType::Jpeg)); assert_eq!(pic.description(), Some("new image")); assert_eq!(pic.data(), b"JPEG data"); } @@ -605,7 +605,7 @@ fn test_picture_stored_after_comment() { let pic = Picture::new_unchecked( PictureType::CoverFront, - MimeType::Png, + Some(MimeType::Png), Some(String::from("blank.png")), BLANK_PNG_DATA.to_vec(), ); @@ -634,7 +634,7 @@ fn test_picture_stored_after_comment() { assert_eq!(pictures.len(), 1); assert_eq!(pictures[0].0.data(), BLANK_PNG_DATA); assert_eq!(pictures[0].0.pic_type(), PictureType::CoverFront); - assert_eq!(pictures[0].0.mime_type(), &MimeType::Png); + assert_eq!(pictures[0].0.mime_type(), Some(&MimeType::Png)); assert_eq!(pictures[0].0.description(), Some("blank.png")); assert_eq!(pictures[0].1.width, 3); assert_eq!(pictures[0].1.height, 2); diff --git a/tests/taglib/test_flacpicture.rs b/tests/taglib/test_flacpicture.rs index 03e49f0c..2480a544 100644 --- a/tests/taglib/test_flacpicture.rs +++ b/tests/taglib/test_flacpicture.rs @@ -25,7 +25,7 @@ fn test_parse() { assert_eq!(info.height, 1); assert_eq!(info.color_depth, 24); assert_eq!(info.num_colors, 0); - assert_eq!(picture.mime_type(), &MimeType::Png); + assert_eq!(picture.mime_type(), Some(&MimeType::Png)); assert_eq!(picture.description(), Some("A pixel.")); assert_eq!(picture.data().len(), 150); } diff --git a/tests/taglib/test_id3v2.rs b/tests/taglib/test_id3v2.rs index c7341147..f3785402 100644 --- a/tests/taglib/test_id3v2.rs +++ b/tests/taglib/test_id3v2.rs @@ -139,7 +139,10 @@ fn test_parse_apic() { Id3v2Version::V4, ) .unwrap(); - assert_eq!(f.picture.mime_type(), &MimeType::Unknown(String::from("m"))); + assert_eq!( + f.picture.mime_type(), + Some(&MimeType::Unknown(String::from("m"))) + ); assert_eq!(f.picture.pic_type(), PictureType::Icon); assert_eq!(f.picture.description(), Some("d")); } @@ -155,7 +158,7 @@ fn test_parse_apic_utf16_bom() { ) .unwrap(); - assert_eq!(f.picture.mime_type(), &MimeType::Jpeg); + assert_eq!(f.picture.mime_type(), Some(&MimeType::Jpeg)); assert_eq!(f.picture.pic_type(), PictureType::Other); assert_eq!(f.picture.description(), Some("cover.jpg")); assert_eq!(f.picture.data(), b"\xff\xd8\xff"); @@ -174,7 +177,7 @@ fn test_parse_apicv22() { ) .unwrap(); - assert_eq!(frame.picture.mime_type(), &MimeType::Jpeg); + assert_eq!(frame.picture.mime_type(), Some(&MimeType::Jpeg)); assert_eq!(frame.picture.pic_type(), PictureType::Icon); assert_eq!(frame.picture.description(), Some("d")); } @@ -185,7 +188,7 @@ fn test_render_apic() { encoding: TextEncoding::UTF8, picture: Picture::new_unchecked( PictureType::CoverBack, - MimeType::Png, + Some(MimeType::Png), Some(String::from("Description")), b"PNG data".to_vec(), ), @@ -929,7 +932,7 @@ fn test_compressed_frame_with_broken_length() { _ => unreachable!(), }; - assert_eq!(picture.mime_type(), &MimeType::Bmp); + assert_eq!(picture.mime_type(), Some(&MimeType::Bmp)); assert_eq!(picture.pic_type(), PictureType::Other); assert!(picture.description().is_none()); assert_eq!(picture.data().len(), 86414); diff --git a/tests/taglib/test_mp4.rs b/tests/taglib/test_mp4.rs index 3c25a4e3..35e70897 100644 --- a/tests/taglib/test_mp4.rs +++ b/tests/taglib/test_mp4.rs @@ -258,9 +258,9 @@ fn test_covr_read() { }; assert!(covrs.next().is_none()); - assert_eq!(picture1.mime_type(), &MimeType::Png); + assert_eq!(picture1.mime_type(), Some(&MimeType::Png)); assert_eq!(picture1.data().len(), 79); - assert_eq!(picture2.mime_type(), &MimeType::Jpeg); + assert_eq!(picture2.mime_type(), Some(&MimeType::Jpeg)); assert_eq!(picture2.data().len(), 287); } @@ -276,7 +276,7 @@ fn test_covr_write() { assert!(tag.contains(&AtomIdent::Fourcc(*b"covr"))); tag.insert_picture(Picture::new_unchecked( PictureType::Other, - MimeType::Png, + Some(MimeType::Png), None, b"foo".to_vec(), )); @@ -300,11 +300,11 @@ fn test_covr_write() { }; assert!(covrs.next().is_none()); - assert_eq!(picture1.mime_type(), &MimeType::Png); + assert_eq!(picture1.mime_type(), Some(&MimeType::Png)); assert_eq!(picture1.data().len(), 79); - assert_eq!(picture2.mime_type(), &MimeType::Jpeg); + assert_eq!(picture2.mime_type(), Some(&MimeType::Jpeg)); assert_eq!(picture2.data().len(), 287); - assert_eq!(picture3.mime_type(), &MimeType::Png); + assert_eq!(picture3.mime_type(), Some(&MimeType::Png)); assert_eq!(picture3.data().len(), 3); } } @@ -323,9 +323,9 @@ fn test_covr_read2() { }; assert!(covrs.next().is_none()); - assert_eq!(picture1.mime_type(), &MimeType::Png); + assert_eq!(picture1.mime_type(), Some(&MimeType::Png)); assert_eq!(picture1.data().len(), 79); - assert_eq!(picture2.mime_type(), &MimeType::Jpeg); + assert_eq!(picture2.mime_type(), Some(&MimeType::Jpeg)); assert_eq!(picture2.data().len(), 287); } @@ -458,9 +458,9 @@ fn test_non_full_meta_atom() { }; assert!(covrs.next().is_none()); - assert_eq!(picture1.mime_type(), &MimeType::Png); + assert_eq!(picture1.mime_type(), Some(&MimeType::Png)); assert_eq!(picture1.data().len(), 79); - assert_eq!(picture2.mime_type(), &MimeType::Jpeg); + assert_eq!(picture2.mime_type(), Some(&MimeType::Jpeg)); assert_eq!(picture2.data().len(), 287); assert_eq!(tag.artist().as_deref(), Some("Test Artist!!!!")); diff --git a/tests/taglib/test_xiphcomment.rs b/tests/taglib/test_xiphcomment.rs index 06811b7d..a100fc23 100644 --- a/tests/taglib/test_xiphcomment.rs +++ b/tests/taglib/test_xiphcomment.rs @@ -115,7 +115,7 @@ fn test_picture() { let picture = Picture::new_unchecked( PictureType::CoverBack, - MimeType::Jpeg, + Some(MimeType::Jpeg), Some(String::from("new image")), b"JPEG data".to_vec(), ); @@ -140,7 +140,7 @@ fn test_picture() { assert_eq!(pictures[0].1.height, 6); assert_eq!(pictures[0].1.color_depth, 16); assert_eq!(pictures[0].1.num_colors, 7); - assert_eq!(pictures[0].0.mime_type(), &MimeType::Jpeg); + assert_eq!(pictures[0].0.mime_type(), Some(&MimeType::Jpeg)); assert_eq!(pictures[0].0.description(), Some("new image")); assert_eq!(pictures[0].0.data(), b"JPEG data"); } From 255916a648369f7f0076150d2416b386c87afed5 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sun, 31 Dec 2023 14:03:04 -0500 Subject: [PATCH 54/75] Tests: Fixup some TagLib tests where TagLib is incorrect --- tests/taglib/test_mp4.rs | 12 ++++++------ tests/taglib/test_speex.rs | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/taglib/test_mp4.rs b/tests/taglib/test_mp4.rs index 35e70897..1bcb7d61 100644 --- a/tests/taglib/test_mp4.rs +++ b/tests/taglib/test_mp4.rs @@ -40,7 +40,7 @@ fn test_properties_aac_without_bitrate() { assert_eq!(f.properties().audio_bitrate(), 3); assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().sample_rate(), 44100); - assert_eq!(f.properties().bit_depth(), Some(16)); + assert_eq!(f.properties().bit_depth(), None); // TagLib reports 16, but the stream is a lossy codec assert!(!f.properties().is_drm_protected()); assert_eq!(f.properties().codec(), &Mp4Codec::AAC); } @@ -50,7 +50,7 @@ fn test_properties_alac() { let f = get_file::("tests/taglib/data/empty_alac.m4a"); assert_eq!(f.properties().duration().as_secs(), 3); assert_eq!(f.properties().duration().as_millis(), 3705); - assert_eq!(f.properties().audio_bitrate(), 3); + assert_eq!(f.properties().audio_bitrate(), 2); // TagLib is off by one (reports 3) assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().sample_rate(), 44100); assert_eq!(f.properties().bit_depth(), Some(16)); @@ -72,10 +72,10 @@ fn test_properties_alac_without_bitrate() { alac_data[i] = 0; } - let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = Mp4File::read_from(&mut std::io::Cursor::new(alac_data), ParseOptions::new()).unwrap(); assert_eq!(f.properties().duration().as_secs(), 3); assert_eq!(f.properties().duration().as_millis(), 3705); - assert_eq!(f.properties().audio_bitrate(), 3); + assert_eq!(f.properties().audio_bitrate(), 2); // TagLib is off by one (reports 3) assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().sample_rate(), 44100); assert_eq!(f.properties().bit_depth(), Some(16)); @@ -88,10 +88,10 @@ fn test_properties_m4v() { let f = get_file::("tests/taglib/data/blank_video.m4v"); assert_eq!(f.properties().duration().as_secs(), 0); assert_eq!(f.properties().duration().as_millis(), 975); - assert_eq!(f.properties().audio_bitrate(), 96); + assert_eq!(f.properties().audio_bitrate(), 95); // TagLib is off by one (reports 96) assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().sample_rate(), 44100); - assert_eq!(f.properties().bit_depth(), Some(16)); + assert_eq!(f.properties().bit_depth(), None); // TagLib reports 16, but the stream is a lossy codec assert!(!f.properties().is_drm_protected()); assert_eq!(f.properties().codec(), &Mp4Codec::AAC); } diff --git a/tests/taglib/test_speex.rs b/tests/taglib/test_speex.rs index 7f9edb33..afc436bf 100644 --- a/tests/taglib/test_speex.rs +++ b/tests/taglib/test_speex.rs @@ -22,6 +22,7 @@ fn test_audio_properties() { // TODO: This test doesn't work, it's very specific with file/packet sizes. Have to determine whether or not to even keep this one. #[test] +#[ignore] fn test_split_packets() { let mut file = temp_file!("tests/taglib/data/empty.spx"); From b23bc2a899af2fdf85a98b2aea2c6f7db25ef21d Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sun, 31 Dec 2023 14:16:49 -0500 Subject: [PATCH 55/75] Tests: Fix bitrate in `TestOGG::testAudioProperties()` --- tests/taglib/test_ogg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/taglib/test_ogg.rs b/tests/taglib/test_ogg.rs index 5cd4d62c..b590ea9b 100644 --- a/tests/taglib/test_ogg.rs +++ b/tests/taglib/test_ogg.rs @@ -78,7 +78,7 @@ fn test_audio_properties() { let f = get_file::("tests/taglib/data/empty.ogg"); assert_eq!(f.properties().duration().as_secs(), 3); assert_eq!(f.properties().duration().as_millis(), 3685); - assert_eq!(f.properties().audio_bitrate(), 1); + assert_eq!(f.properties().audio_bitrate(), 112); // TagLib reports 1? That is not correct. assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().sample_rate(), 44100); assert_eq!(f.properties().version(), 0); From 770f6d2716438b7b78c6ef74291de64d48701b9b Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Wed, 3 Jan 2024 09:53:47 -0500 Subject: [PATCH 56/75] Tests: Ignore TagLib `TestMP4::testUpdateStco()` for now --- tests/taglib/test_mp4.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/taglib/test_mp4.rs b/tests/taglib/test_mp4.rs index 1bcb7d61..eceec7aa 100644 --- a/tests/taglib/test_mp4.rs +++ b/tests/taglib/test_mp4.rs @@ -137,8 +137,28 @@ fn test_is_empty() { } #[test] +#[ignore] // TODO: The atom parsing internals are not exposed yet fn test_update_stco() { - todo!("We don't update stco atoms") + let mut file = temp_file!("no-tags.3g2"); + + { + let mut f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut tag = Ilst::default(); + tag.set_artist("X".repeat(3000)); + f.set_ilst(tag); + + // Find and collect all `stco` offsets + + f.save_to(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let _f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + + // Find and collect all `stco` offsets, compare with previous + } } #[test] From 455ee0793a8a338252bea1c146b061336e4e7deb Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Fri, 5 Apr 2024 13:12:22 -0400 Subject: [PATCH 57/75] Tests: Update TagLib tests for latest changes --- tests/taglib/test_aiff.rs | 6 +- tests/taglib/test_ape.rs | 24 +++++-- tests/taglib/test_apetag.rs | 6 +- tests/taglib/test_fileref.rs | 112 ++++++++++++++++--------------- tests/taglib/test_flac.rs | 44 ++++++------ tests/taglib/test_id3v2.rs | 12 ++-- tests/taglib/test_mp4.rs | 23 ++++--- tests/taglib/test_mpc.rs | 12 ++-- tests/taglib/test_mpeg.rs | 17 +++-- tests/taglib/test_ogaflac.rs | 4 +- tests/taglib/test_ogg.rs | 16 ++--- tests/taglib/test_opus.rs | 4 +- tests/taglib/test_speex.rs | 6 +- tests/taglib/test_wav.rs | 16 ++--- tests/taglib/test_wavpack.rs | 10 +-- tests/taglib/test_xiphcomment.rs | 7 +- 16 files changed, 172 insertions(+), 147 deletions(-) diff --git a/tests/taglib/test_aiff.rs b/tests/taglib/test_aiff.rs index d30a3096..d0854d25 100644 --- a/tests/taglib/test_aiff.rs +++ b/tests/taglib/test_aiff.rs @@ -5,7 +5,7 @@ use std::io::Seek; use lofty::id3::v2::Id3v2Tag; use lofty::iff::aiff::{AiffCompressionType, AiffFile}; -use lofty::{Accessor, AudioFile, FileType, ParseOptions, Probe}; +use lofty::{Accessor, AudioFile, FileType, ParseOptions, Probe, WriteOptions}; #[test] fn test_aiff_properties() { @@ -65,7 +65,7 @@ fn test_save_id3v2() { id3v2.set_title("TitleXXX".to_string()); tfile.set_id3v2(id3v2); file.rewind().unwrap(); - tfile.save_to(&mut file).unwrap(); + tfile.save_to(&mut file, WriteOptions::default()).unwrap(); assert!(tfile.contains_tag_type(lofty::TagType::Id3v2)); } @@ -81,7 +81,7 @@ fn test_save_id3v2() { id3v2.remove_title(); tfile.set_id3v2(id3v2); file.rewind().unwrap(); - tfile.save_to(&mut file).unwrap(); + tfile.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); diff --git a/tests/taglib/test_ape.rs b/tests/taglib/test_ape.rs index 09c74d8a..dc248733 100644 --- a/tests/taglib/test_ape.rs +++ b/tests/taglib/test_ape.rs @@ -6,7 +6,7 @@ use std::time::Duration; use lofty::ape::{ApeFile, ApeItem, ApeTag}; use lofty::id3::v1::Id3v1Tag; -use lofty::{Accessor, AudioFile, FileType, ItemValue, ParseOptions, Probe, TagExt}; +use lofty::{Accessor, AudioFile, FileType, ItemValue, ParseOptions, Probe, TagExt, WriteOptions}; fn test_399(path: &str) { let f = get_file::(path); @@ -106,7 +106,9 @@ fn test_strip_and_properties() { ape_file.set_id3v1(id3v1_tag); file.rewind().unwrap(); - ape_file.save_to(&mut file).unwrap(); + ape_file + .save_to(&mut file, WriteOptions::default()) + .unwrap(); } { file.rewind().unwrap(); @@ -337,7 +339,11 @@ fn test_properties() { ape_file.set_ape(tag.clone()); file.rewind().unwrap(); - ape_file.ape().unwrap().save_to(&mut file).unwrap(); + ape_file + .ape() + .unwrap() + .save_to(&mut file, WriteOptions::default()) + .unwrap(); } { file.rewind().unwrap(); @@ -358,11 +364,15 @@ fn test_repeated_save() { let mut ape_tag = ApeTag::default(); ape_tag.set_title(String::from("01234 56789 ABCDE FGHIJ")); ape_file.set_ape(ape_tag); - ape_file.save_to(&mut file).unwrap(); + ape_file + .save_to(&mut file, WriteOptions::default()) + .unwrap(); file.rewind().unwrap(); ape_file.ape_mut().unwrap().set_title(String::from("0")); - ape_file.save_to(&mut file).unwrap(); + ape_file + .save_to(&mut file, WriteOptions::default()) + .unwrap(); file.rewind().unwrap(); let mut id3v1_tag = Id3v1Tag::default(); @@ -371,7 +381,9 @@ fn test_repeated_save() { ape_file.ape_mut().unwrap().set_title(String::from( "01234 56789 ABCDE FGHIJ 01234 56789 ABCDE FGHIJ 01234 56789", )); - ape_file.save_to(&mut file).unwrap(); + ape_file + .save_to(&mut file, WriteOptions::default()) + .unwrap(); } { file.rewind().unwrap(); diff --git a/tests/taglib/test_apetag.rs b/tests/taglib/test_apetag.rs index 2bc591ab..077e838e 100644 --- a/tests/taglib/test_apetag.rs +++ b/tests/taglib/test_apetag.rs @@ -4,7 +4,7 @@ use std::io::Seek; use lofty::ape::{ApeItem, ApeTag}; use lofty::musepack::MpcFile; -use lofty::{Accessor, AudioFile, ItemValue, ParseOptions, TagExt}; +use lofty::{Accessor, AudioFile, ItemValue, ParseOptions, TagExt, WriteOptions}; #[test] fn test_is_empty() { @@ -104,7 +104,9 @@ fn test_id3v1_collision() { ape_tag.set_artist(String::from("Filltointersect ")); ape_tag.set_title(String::from("Filltointersect ")); mpc_file.set_ape(ape_tag); - mpc_file.save_to(&mut file).unwrap(); + mpc_file + .save_to(&mut file, WriteOptions::default()) + .unwrap(); } { file.rewind().unwrap(); diff --git a/tests/taglib/test_fileref.rs b/tests/taglib/test_fileref.rs index 4eaff6d2..d11db9b6 100644 --- a/tests/taglib/test_fileref.rs +++ b/tests/taglib/test_fileref.rs @@ -6,8 +6,8 @@ use std::io::{Read, Seek}; use lofty::error::ErrorKind; use lofty::resolve::FileResolver; use lofty::{ - Accessor, AudioFile, FileProperties, FileType, ParseOptions, Tag, TagExt, TagType, TaggedFile, - TaggedFileExt, + Accessor, AudioFile, FileProperties, FileType, GlobalOptions, ParseOptions, Tag, TagExt, + TagType, TaggedFile, TaggedFileExt, WriteOptions, }; fn file_ref_save(path: &str, expected_file_type: FileType) { @@ -33,7 +33,7 @@ fn file_ref_save(path: &str, expected_file_type: FileType) { tag.set_comment(String::from("a comment")); tag.set_track(5); tag.set_year(2020); - tag.save_to(&mut file).unwrap(); + tag.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -55,7 +55,7 @@ fn file_ref_save(path: &str, expected_file_type: FileType) { tag.set_comment(String::from("another comment")); tag.set_track(7); tag.set_year(2080); - tag.save_to(&mut file).unwrap(); + tag.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -214,72 +214,76 @@ fn test_default_file_extensions() { // Marker test, Lofty does not replicate this API } -// TODO: We need to check resolvers *first* and then resort to our default implementations -#[test] -#[ignore] -fn test_file_resolver() { - { - let file = lofty::read_from_path("tests/taglib/data/xing.mp3").unwrap(); - assert_eq!(file.file_type(), FileType::Mpeg); - } - - struct DummyResolver; - impl Into for DummyResolver { - fn into(self) -> TaggedFile { - TaggedFile::new(FileType::Vorbis, FileProperties::default(), Vec::new()) - } - } +use rusty_fork::rusty_fork_test; - impl AudioFile for DummyResolver { - type Properties = (); +rusty_fork_test! { + #[test] + fn test_file_resolver() { + lofty::apply_global_options(GlobalOptions::new().use_custom_resolvers(true)); - fn read_from(_: &mut R, _: ParseOptions) -> lofty::Result - where - R: Read + Seek, - Self: Sized, { - Ok(Self) + let file = lofty::read_from_path("tests/taglib/data/xing.mp3").unwrap(); + assert_eq!(file.file_type(), FileType::Mpeg); } - fn save_to(&self, _: &mut File) -> lofty::Result<()> { - unimplemented!() + struct DummyResolver; + impl Into for DummyResolver { + fn into(self) -> TaggedFile { + TaggedFile::new(FileType::Vorbis, FileProperties::default(), Vec::new()) + } } - fn properties(&self) -> &Self::Properties { - unimplemented!() - } + impl AudioFile for DummyResolver { + type Properties = (); - fn contains_tag(&self) -> bool { - unimplemented!() - } + fn read_from(_: &mut R, _: ParseOptions) -> lofty::Result + where + R: Read + Seek, + Self: Sized, + { + Ok(Self) + } - fn contains_tag_type(&self, _: TagType) -> bool { - unimplemented!() - } - } + fn save_to(&self, _: &mut File, _: WriteOptions) -> lofty::Result<()> { + unimplemented!() + } - impl FileResolver for DummyResolver { - fn extension() -> Option<&'static str> { - Some("mp3") - } + fn properties(&self) -> &Self::Properties { + unimplemented!() + } - fn primary_tag_type() -> TagType { - unimplemented!() - } + fn contains_tag(&self) -> bool { + unimplemented!() + } - fn supported_tag_types() -> &'static [TagType] { - unimplemented!() + fn contains_tag_type(&self, _: TagType) -> bool { + unimplemented!() + } } - fn guess(_: &[u8]) -> Option { - Some(FileType::Vorbis) + impl FileResolver for DummyResolver { + fn extension() -> Option<&'static str> { + Some("mp3") + } + + fn primary_tag_type() -> TagType { + unimplemented!() + } + + fn supported_tag_types() -> &'static [TagType] { + unimplemented!() + } + + fn guess(_: &[u8]) -> Option { + Some(FileType::Vorbis) + } } - } - lofty::resolve::register_custom_resolver::("Dummy"); + lofty::resolve::register_custom_resolver::("Dummy"); - { - let file = lofty::read_from_path("tests/taglib/data/xing.mp3").unwrap(); - assert_eq!(file.file_type(), FileType::Vorbis); + { + let file = lofty::read_from_path("tests/taglib/data/xing.mp3").unwrap(); + assert_eq!(file.file_type(), FileType::Vorbis); + } } } diff --git a/tests/taglib/test_flac.rs b/tests/taglib/test_flac.rs index cdccbf12..8af0805c 100644 --- a/tests/taglib/test_flac.rs +++ b/tests/taglib/test_flac.rs @@ -8,6 +8,7 @@ use lofty::id3::v2::Id3v2Tag; use lofty::ogg::{OggPictureStorage, VorbisComments}; use lofty::{ Accessor, AudioFile, MimeType, ParseOptions, Picture, PictureInformation, PictureType, TagExt, + WriteOptions, }; #[test] @@ -69,7 +70,7 @@ fn test_add_picture() { }; f.insert_picture(new_pic, Some(new_pic_info)).unwrap(); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -126,7 +127,7 @@ fn test_replace_picture() { f.remove_pictures(); f.insert_picture(new_pic, Some(new_pic_info)).unwrap(); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -159,7 +160,7 @@ fn test_remove_all_pictures() { assert_eq!(lst.len(), 1); f.remove_pictures(); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -185,7 +186,7 @@ fn test_repeated_save_1() { f.vorbis_comments_mut() .unwrap() .set_title(String::from("NEW TITLE")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); file.rewind().unwrap(); assert_eq!( @@ -195,7 +196,7 @@ fn test_repeated_save_1() { f.vorbis_comments_mut() .unwrap() .set_title(String::from("NEW TITLE 2")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); assert_eq!( f.vorbis_comments().unwrap().title().as_deref(), @@ -232,10 +233,10 @@ fn test_repeated_save_3() { tag.set_title(String::from_utf8(vec![b'X'; 8 * 1024]).unwrap()); f.set_vorbis_comments(tag); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); file.rewind().unwrap(); assert_eq!(file.metadata().unwrap().len(), 12862); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); assert_eq!(file.metadata().unwrap().len(), 12862); } @@ -252,7 +253,7 @@ fn test_save_multiple_values() { f.vorbis_comments_mut() .unwrap() .push(String::from("ARTIST"), String::from("artist 2")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -347,7 +348,10 @@ fn test_properties() { f.set_vorbis_comments(tag.clone()); file.rewind().unwrap(); - f.vorbis_comments().unwrap().save_to(&mut file).unwrap(); + f.vorbis_comments() + .unwrap() + .save_to(&mut file, WriteOptions::default()) + .unwrap(); } file.rewind().unwrap(); { @@ -405,7 +409,7 @@ fn test_zero_sized_padding_2() { f.vorbis_comments_mut() .unwrap() .set_title(String::from("ABC")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -415,7 +419,7 @@ fn test_zero_sized_padding_2() { f.vorbis_comments_mut() .unwrap() .set_title(String::from_utf8(vec![b'X'; 3067]).unwrap()); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -435,7 +439,7 @@ fn test_shrink_padding() { f.vorbis_comments_mut() .unwrap() .set_title(String::from_utf8(vec![b'X'; 128 * 1024]).unwrap()); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); assert!(file.metadata().unwrap().len() > 128 * 1024); } file.rewind().unwrap(); @@ -446,7 +450,7 @@ fn test_shrink_padding() { f.vorbis_comments_mut() .unwrap() .set_title(String::from("0123456789")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); assert!(file.metadata().unwrap().len() < 8 * 1024); } } @@ -471,7 +475,7 @@ fn test_empty_id3v2() { file.rewind().unwrap(); f.set_id3v2(Id3v2Tag::default()); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -494,7 +498,7 @@ fn test_strip_tags() { f.vorbis_comments_mut() .unwrap() .set_title(String::from("XiphComment Title")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -507,7 +511,7 @@ fn test_strip_tags() { Some("XiphComment Title") ); f.remove_vorbis_comments(); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -533,7 +537,7 @@ fn test_remove_xiph_field() { f.vorbis_comments_mut() .unwrap() .set_title(String::from("XiphComment Title")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -545,7 +549,7 @@ fn test_remove_xiph_field() { Some("XiphComment Title") ); let _ = f.vorbis_comments_mut().unwrap().remove("TITLE"); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -566,7 +570,7 @@ fn test_empty_seek_table() { let mut tag = VorbisComments::default(); tag.set_title(String::from("XiphComment Title")); f.set_vorbis_comments(tag); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -620,7 +624,7 @@ fn test_picture_stored_after_comment() { let mut tag = VorbisComments::default(); tag.set_title(String::from("Title")); f.set_vorbis_comments(tag); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { diff --git a/tests/taglib/test_id3v2.rs b/tests/taglib/test_id3v2.rs index f3785402..ad2ea51c 100644 --- a/tests/taglib/test_id3v2.rs +++ b/tests/taglib/test_id3v2.rs @@ -15,7 +15,7 @@ use lofty::id3::v2::{ use lofty::mpeg::MpegFile; use lofty::{ Accessor, AudioFile, MimeType, ParseOptions, ParsingMode, Picture, PictureType, TagExt, - TextEncoding, + TextEncoding, WriteOptions, }; #[test] @@ -317,7 +317,7 @@ fn test_popm_from_file() { Frame::new("POPM", FrameValue::Popularimeter(f), FrameFlags::default()).unwrap(), ); foo.set_id3v2(tag); - foo.save_to(&mut file).unwrap(); + foo.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -839,7 +839,7 @@ fn test_save_utf16_comment() { .unwrap(), ); foo.set_id3v2(tag); - foo.save_to(&mut file).unwrap(); + foo.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -995,7 +995,7 @@ fn test_delete_frame() { let t = f.id3v2_mut().unwrap(); let _ = t.remove(&FrameId::Valid(Cow::Borrowed("TCON"))); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -1016,7 +1016,7 @@ fn test_save_and_strip_id3v1_should_not_add_frame_from_id3v1_to_id3v2() { let mut tag = Id3v2Tag::new(); tag.set_artist(String::from("Artist")); foo.set_id3v2(tag); - foo.save_to(&mut file).unwrap(); + foo.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -1028,7 +1028,7 @@ fn test_save_and_strip_id3v1_should_not_add_frame_from_id3v1_to_id3v2() { .unwrap() .remove(&FrameId::Valid(Cow::Borrowed("TPE1"))); - bar.save_to(&mut file).unwrap(); + bar.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); diff --git a/tests/taglib/test_mp4.rs b/tests/taglib/test_mp4.rs index eceec7aa..adc844c6 100644 --- a/tests/taglib/test_mp4.rs +++ b/tests/taglib/test_mp4.rs @@ -5,7 +5,10 @@ use std::borrow::Cow; use std::io::{Read, Seek}; use lofty::mp4::{Atom, AtomData, AtomIdent, Ilst, Mp4Codec, Mp4File}; -use lofty::{Accessor, AudioFile, MimeType, ParseOptions, Picture, PictureType, TagExt, TagType}; +use lofty::{ + Accessor, AudioFile, MimeType, ParseOptions, Picture, PictureType, TagExt, TagType, + WriteOptions, +}; #[test] fn test_properties_aac() { @@ -119,7 +122,7 @@ fn test_has_tag() { let mut tag = Ilst::default(); tag.set_title(String::from("TITLE")); f.set_ilst(tag); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -151,7 +154,7 @@ fn test_update_stco() { // Find and collect all `stco` offsets - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -181,7 +184,7 @@ fn test_freeform() { }, AtomData::UTF8(String::from("Bar")), )); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -203,7 +206,7 @@ fn test_freeform() { .next(), Some(&AtomData::UTF8(String::from("Bar"))) ); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } } @@ -229,7 +232,7 @@ fn test_save_existing_when_ilst_is_last() { ); assert_eq!(ilst.artist().as_deref(), Some("Pearl Jam")); ilst.set_comment(String::from("foo")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -300,7 +303,7 @@ fn test_covr_write() { None, b"foo".to_vec(), )); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -382,9 +385,9 @@ fn test_repeated_save() { tag.set_title(String::from("0123456789")); f.set_ilst(tag); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); file.rewind().unwrap(); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); file.rewind().unwrap(); let mut file_bytes = Vec::new(); @@ -425,7 +428,7 @@ fn test_remove_metadata() { assert!(tag.is_empty()); tag.set_title(String::from("TITLE")); f.set_ilst(tag); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { diff --git a/tests/taglib/test_mpc.rs b/tests/taglib/test_mpc.rs index 6aa10022..145e5361 100644 --- a/tests/taglib/test_mpc.rs +++ b/tests/taglib/test_mpc.rs @@ -6,7 +6,7 @@ use std::io::Seek; use lofty::ape::ApeTag; use lofty::id3::v1::Id3v1Tag; use lofty::musepack::{MpcFile, MpcProperties}; -use lofty::{Accessor, AudioFile, ParseOptions, Probe, TagExt}; +use lofty::{Accessor, AudioFile, ParseOptions, Probe, TagExt, WriteOptions}; #[test] fn test_properties_sv8() { @@ -131,7 +131,7 @@ fn test_strip_and_properties() { let mut id3v1 = Id3v1Tag::new(); id3v1.set_title(String::from("ID3v1")); f.set_id3v1(id3v1); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -142,7 +142,7 @@ fn test_strip_and_properties() { f.ape_mut().unwrap().clear(); assert_eq!(f.id3v1().unwrap().title().as_deref(), Some("ID3v1")); f.id3v1_mut().unwrap().clear(); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -168,12 +168,12 @@ fn test_repeated_save() { ape.set_title(String::from("01234 56789 ABCDE FGHIJ")); f.set_ape(ape); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); file.rewind().unwrap(); f.ape_mut().unwrap().set_title(String::from("0")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); file.rewind().unwrap(); let mut id3v1 = Id3v1Tag::new(); @@ -182,7 +182,7 @@ fn test_repeated_save() { f.ape_mut().unwrap().set_title(String::from( "01234 56789 ABCDE FGHIJ 01234 56789 ABCDE FGHIJ 01234 56789", )); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { diff --git a/tests/taglib/test_mpeg.rs b/tests/taglib/test_mpeg.rs index d3dda286..23173ba1 100644 --- a/tests/taglib/test_mpeg.rs +++ b/tests/taglib/test_mpeg.rs @@ -8,7 +8,7 @@ use lofty::ape::ApeTag; use lofty::id3::v1::Id3v1Tag; use lofty::id3::v2::{Id3v2Tag, Id3v2Version}; use lofty::mpeg::MpegFile; -use lofty::{Accessor, AudioFile, ParseOptions}; +use lofty::{Accessor, AudioFile, ParseOptions, WriteOptions}; #[test] fn test_audio_properties_xing_header_cbr() { @@ -117,7 +117,7 @@ fn test_save_id3v24() { tag.set_title(xxx.clone()); tag.set_artist(String::from("Artist A")); f.set_id3v2(tag); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -150,7 +150,7 @@ fn test_save_id3v23() { tag.set_title(xxx.clone()); tag.set_artist(String::from("Artist A")); f.set_id3v2(tag); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -196,7 +196,7 @@ fn test_strip_and_properties() { let mut id3v1 = Id3v1Tag::default(); id3v1.set_title(String::from("ID3v1")); f.set_id3v1(id3v1); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -240,12 +240,12 @@ fn test_repeated_save_3() { let mut ape = ApeTag::default(); ape.set_title(String::from("01234 56789 ABCDE FGHIJ")); f.set_ape(ape); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { f.ape_mut().unwrap().set_title(String::from("0")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } { let mut id3v1 = Id3v1Tag::default(); @@ -257,7 +257,7 @@ fn test_repeated_save_3() { f.ape_mut().unwrap().set_title(String::from( "01234 56789 ABCDE FGHIJ 01234 56789 ABCDE FGHIJ 01234 56789", )); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } } file.rewind().unwrap(); @@ -286,7 +286,6 @@ fn test_empty_ape() { // Marker test, Lofty accepts empty strings as valid values } -// TODO: We can't find an ID3v2 tag after saving with garbage #[test] fn test_ignore_garbage() { let mut file = temp_file!("tests/taglib/data/garbage.mp3"); @@ -298,7 +297,7 @@ fn test_ignore_garbage() { assert_eq!(f.id3v2().unwrap().title().as_deref(), Some("Title A")); f.id3v2_mut().unwrap().set_title(String::from("Title B")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { diff --git a/tests/taglib/test_ogaflac.rs b/tests/taglib/test_ogaflac.rs index fd404bc3..ad749206 100644 --- a/tests/taglib/test_ogaflac.rs +++ b/tests/taglib/test_ogaflac.rs @@ -4,7 +4,7 @@ use std::io::{Seek, SeekFrom}; use lofty::flac::FlacFile; use lofty::ogg::VorbisComments; -use lofty::{Accessor, AudioFile, ParseOptions}; +use lofty::{Accessor, AudioFile, ParseOptions, WriteOptions}; // TODO: We don't support FLAC in OGA (#172) #[test] @@ -19,7 +19,7 @@ fn test_framing_bit() { let mut vorbis_comments = VorbisComments::new(); vorbis_comments.set_artist(String::from("The Artist")); f.set_vorbis_comments(vorbis_comments); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { diff --git a/tests/taglib/test_ogg.rs b/tests/taglib/test_ogg.rs index b590ea9b..9111343e 100644 --- a/tests/taglib/test_ogg.rs +++ b/tests/taglib/test_ogg.rs @@ -5,7 +5,7 @@ use std::io::{Read, Seek, SeekFrom}; use byteorder::{LittleEndian, ReadBytesExt}; use lofty::ogg::VorbisFile; -use lofty::{Accessor, AudioFile, ParseOptions}; +use lofty::{Accessor, AudioFile, ParseOptions, WriteOptions}; #[test] fn test_simple() { @@ -17,7 +17,7 @@ fn test_simple() { f.vorbis_comments_mut() .set_artist(String::from("The Artist")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -42,7 +42,7 @@ fn test_split_packets2() { file.rewind().unwrap(); f.vorbis_comments_mut().set_title(text.clone()); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -52,7 +52,7 @@ fn test_split_packets2() { assert_eq!(f.vorbis_comments().title().as_deref(), Some(&*text)); f.vorbis_comments_mut().set_title(String::from("ABCDE")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -99,7 +99,7 @@ fn test_page_checksum() { f.vorbis_comments_mut() .set_title(String::from("The Artist")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); file.seek(SeekFrom::Start(0x50)).unwrap(); assert_eq!(file.read_u32::().unwrap(), 0x3D3BD92D); @@ -111,7 +111,7 @@ fn test_page_checksum() { f.vorbis_comments_mut() .set_title(String::from("The Artist 2")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); file.seek(SeekFrom::Start(0x50)).unwrap(); assert_eq!(file.read_u32::().unwrap(), 0xD985291C); @@ -130,7 +130,7 @@ fn test_page_granule_position() { // check if the granule position is -1 indicating that no packets // finish on this page. f.vorbis_comments_mut().set_comment("A".repeat(70000)); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); file.seek(SeekFrom::Start(0x3A)).unwrap(); let mut buf = [0; 6]; @@ -147,7 +147,7 @@ fn test_page_granule_position() { // check if the granule position is zero. f.vorbis_comments_mut() .set_comment(String::from("A small comment")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); file.seek(SeekFrom::Start(0x3A)).unwrap(); let mut buf = [0; 6]; diff --git a/tests/taglib/test_opus.rs b/tests/taglib/test_opus.rs index ec55612e..71771ee8 100644 --- a/tests/taglib/test_opus.rs +++ b/tests/taglib/test_opus.rs @@ -4,7 +4,7 @@ use crate::util::get_file; use std::io::Seek; use lofty::ogg::OpusFile; -use lofty::{Accessor, AudioFile, ParseOptions}; +use lofty::{Accessor, AudioFile, ParseOptions, WriteOptions}; #[test] fn test_audio_properties() { @@ -38,7 +38,7 @@ fn test_write_comments() { file.rewind().unwrap(); f.vorbis_comments_mut() .set_artist(String::from("Your Tester")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { diff --git a/tests/taglib/test_speex.rs b/tests/taglib/test_speex.rs index afc436bf..1c914c43 100644 --- a/tests/taglib/test_speex.rs +++ b/tests/taglib/test_speex.rs @@ -4,7 +4,7 @@ use crate::util::get_file; use std::io::Seek; use lofty::ogg::{SpeexFile, VorbisComments}; -use lofty::{Accessor, AudioFile, ParseOptions}; +use lofty::{Accessor, AudioFile, ParseOptions, WriteOptions}; #[test] fn test_audio_properties() { @@ -34,7 +34,7 @@ fn test_split_packets() { let mut tag = VorbisComments::default(); tag.set_title(text.clone()); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -56,7 +56,7 @@ fn test_split_packets() { f.vorbis_comments_mut().set_title(String::from("ABCDE")); file.rewind().unwrap(); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { diff --git a/tests/taglib/test_wav.rs b/tests/taglib/test_wav.rs index 361ae86e..e909ae20 100644 --- a/tests/taglib/test_wav.rs +++ b/tests/taglib/test_wav.rs @@ -2,7 +2,7 @@ use crate::temp_file; use crate::util::get_file; use lofty::id3::v2::Id3v2Tag; use lofty::iff::wav::{RIFFInfoList, WavFile, WavFormat}; -use lofty::{Accessor, AudioFile, ParseOptions, TagType}; +use lofty::{Accessor, AudioFile, ParseOptions, TagType, WriteOptions}; use std::io::{Cursor, Read, Seek, SeekFrom}; #[test] @@ -82,7 +82,7 @@ fn test_id3v2_tag() { id3v2.set_title(String::from("Title")); id3v2.set_artist(String::from("Artist")); f.set_id3v2(id3v2); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); assert!(f.id3v2().is_some()); } file.rewind().unwrap(); @@ -96,7 +96,7 @@ fn test_id3v2_tag() { f.id3v2_mut().unwrap().remove_title(); f.id3v2_mut().unwrap().remove_artist(); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -145,7 +145,7 @@ fn test_info_tag() { riff_info.set_artist(String::from("Artist")); f.set_riff_info(riff_info); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -158,7 +158,7 @@ fn test_info_tag() { f.riff_info_mut().unwrap().remove_title(); f.riff_info_mut().unwrap().remove_artist(); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -183,7 +183,7 @@ fn test_strip_tags() { riff_info.set_title(String::from("test title")); f.set_riff_info(riff_info); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -205,7 +205,7 @@ fn test_strip_tags() { riff_info.set_title(String::from("test title")); f.set_riff_info(riff_info); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -241,7 +241,7 @@ fn test_duplicate_tags() { assert!(f.riff_info().is_some()); assert_eq!(f.riff_info().unwrap().title().as_deref(), Some("Title1")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); assert_eq!(file.seek(SeekFrom::End(0)).unwrap(), 15898); let mut file_bytes = Vec::new(); diff --git a/tests/taglib/test_wavpack.rs b/tests/taglib/test_wavpack.rs index aab50175..b0288a77 100644 --- a/tests/taglib/test_wavpack.rs +++ b/tests/taglib/test_wavpack.rs @@ -7,7 +7,7 @@ use std::io::Seek; use lofty::ape::ApeTag; use lofty::id3::v1::Id3v1Tag; use lofty::wavpack::WavPackFile; -use lofty::{Accessor, AudioFile, ParseOptions}; +use lofty::{Accessor, AudioFile, ParseOptions, WriteOptions}; #[test] fn test_no_length_properties() { @@ -96,7 +96,7 @@ fn test_strip_and_properties() { id3v1.set_title(String::from("ID3v1")); f.set_id3v1(id3v1); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -124,11 +124,11 @@ fn test_repeated_save() { let mut ape = ApeTag::default(); ape.set_title(String::from("01234 56789 ABCDE FGHIJ")); f.set_ape(ape); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); file.rewind().unwrap(); f.ape_mut().unwrap().set_title(String::from("0")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); file.rewind().unwrap(); let mut id3v1 = Id3v1Tag::default(); @@ -137,7 +137,7 @@ fn test_repeated_save() { f.ape_mut().unwrap().set_title(String::from( "01234 56789 ABCDE FGHIJ 01234 56789 ABCDE FGHIJ 01234 56789", )); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { diff --git a/tests/taglib/test_xiphcomment.rs b/tests/taglib/test_xiphcomment.rs index a100fc23..af46839f 100644 --- a/tests/taglib/test_xiphcomment.rs +++ b/tests/taglib/test_xiphcomment.rs @@ -5,6 +5,7 @@ use std::io::Seek; use lofty::ogg::{OggPictureStorage, VorbisComments, VorbisFile}; use lofty::{ Accessor, AudioFile, MimeType, ParseOptions, Picture, PictureInformation, PictureType, TagExt, + WriteOptions, }; #[test] @@ -80,7 +81,7 @@ fn test_clear_comment() { f.vorbis_comments_mut() .push(String::from("COMMENT"), String::from("Comment1")); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -129,7 +130,7 @@ fn test_picture() { f.vorbis_comments_mut() .insert_picture(picture, Some(info)) .unwrap(); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { @@ -157,7 +158,7 @@ fn test_lowercase_fields() { assert_eq!(f.vorbis_comments().title().as_deref(), Some("TEST TITLE")); assert_eq!(f.vorbis_comments().artist().as_deref(), Some("TEST ARTIST")); assert_eq!(f.vorbis_comments().pictures().len(), 1); - f.save_to(&mut file).unwrap(); + f.save_to(&mut file, WriteOptions::default()).unwrap(); } file.rewind().unwrap(); { From 9051604ad37c6bf81e8d863f7c9a31cb440d3d0c Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sun, 14 Apr 2024 13:33:28 -0400 Subject: [PATCH 58/75] Tests: Update TagLib tests for new module structure --- tests/taglib/test_aiff.rs | 9 ++++++--- tests/taglib/test_ape.rs | 5 ++++- tests/taglib/test_apetag.rs | 4 +++- tests/taglib/test_fileref.rs | 14 +++++++------- tests/taglib/test_flac.rs | 8 ++++---- tests/taglib/test_flacpicture.rs | 3 ++- tests/taglib/test_id3v2.rs | 9 +++++---- tests/taglib/test_info.rs | 2 +- tests/taglib/test_mp4.rs | 8 ++++---- tests/taglib/test_mpc.rs | 5 ++++- tests/taglib/test_mpeg.rs | 4 +++- tests/taglib/test_ogaflac.rs | 4 +++- tests/taglib/test_ogg.rs | 4 +++- tests/taglib/test_opus.rs | 4 +++- tests/taglib/test_speex.rs | 7 ++++--- tests/taglib/test_wav.rs | 5 ++++- tests/taglib/test_wavpack.rs | 4 +++- tests/taglib/test_xiphcomment.rs | 8 ++++---- tests/taglib/util/mod.rs | 3 ++- 19 files changed, 69 insertions(+), 41 deletions(-) diff --git a/tests/taglib/test_aiff.rs b/tests/taglib/test_aiff.rs index d0854d25..1a5df78c 100644 --- a/tests/taglib/test_aiff.rs +++ b/tests/taglib/test_aiff.rs @@ -3,9 +3,12 @@ use crate::{assert_delta, temp_file}; use std::io::Seek; +use lofty::config::{ParseOptions, WriteOptions}; +use lofty::file::{AudioFile, FileType}; use lofty::id3::v2::Id3v2Tag; use lofty::iff::aiff::{AiffCompressionType, AiffFile}; -use lofty::{Accessor, AudioFile, FileType, ParseOptions, Probe, WriteOptions}; +use lofty::probe::Probe; +use lofty::tag::{Accessor, TagType}; #[test] fn test_aiff_properties() { @@ -66,7 +69,7 @@ fn test_save_id3v2() { tfile.set_id3v2(id3v2); file.rewind().unwrap(); tfile.save_to(&mut file, WriteOptions::default()).unwrap(); - assert!(tfile.contains_tag_type(lofty::TagType::Id3v2)); + assert!(tfile.contains_tag_type(TagType::Id3v2)); } file.rewind().unwrap(); @@ -88,7 +91,7 @@ fn test_save_id3v2() { { let tfile = AiffFile::read_from(&mut file, ParseOptions::new()).unwrap(); - assert!(!tfile.contains_tag_type(lofty::TagType::Id3v2)); + assert!(!tfile.contains_tag_type(TagType::Id3v2)); } } diff --git a/tests/taglib/test_ape.rs b/tests/taglib/test_ape.rs index dc248733..8c167831 100644 --- a/tests/taglib/test_ape.rs +++ b/tests/taglib/test_ape.rs @@ -5,8 +5,11 @@ use std::io::Seek; use std::time::Duration; use lofty::ape::{ApeFile, ApeItem, ApeTag}; +use lofty::config::{ParseOptions, WriteOptions}; +use lofty::file::{AudioFile, FileType}; use lofty::id3::v1::Id3v1Tag; -use lofty::{Accessor, AudioFile, FileType, ItemValue, ParseOptions, Probe, TagExt, WriteOptions}; +use lofty::probe::Probe; +use lofty::tag::{Accessor, ItemValue, TagExt}; fn test_399(path: &str) { let f = get_file::(path); diff --git a/tests/taglib/test_apetag.rs b/tests/taglib/test_apetag.rs index 077e838e..981c100d 100644 --- a/tests/taglib/test_apetag.rs +++ b/tests/taglib/test_apetag.rs @@ -3,8 +3,10 @@ use crate::temp_file; use std::io::Seek; use lofty::ape::{ApeItem, ApeTag}; +use lofty::config::{ParseOptions, WriteOptions}; +use lofty::file::AudioFile; use lofty::musepack::MpcFile; -use lofty::{Accessor, AudioFile, ItemValue, ParseOptions, TagExt, WriteOptions}; +use lofty::tag::{Accessor, ItemValue, TagExt}; #[test] fn test_is_empty() { diff --git a/tests/taglib/test_fileref.rs b/tests/taglib/test_fileref.rs index d11db9b6..722ce5f7 100644 --- a/tests/taglib/test_fileref.rs +++ b/tests/taglib/test_fileref.rs @@ -3,12 +3,11 @@ use crate::temp_file; use std::fs::File; use std::io::{Read, Seek}; +use lofty::config::{GlobalOptions, ParseOptions, WriteOptions}; use lofty::error::ErrorKind; +use lofty::file::{AudioFile, FileType, TaggedFile, TaggedFileExt}; use lofty::resolve::FileResolver; -use lofty::{ - Accessor, AudioFile, FileProperties, FileType, GlobalOptions, ParseOptions, Tag, TagExt, - TagType, TaggedFile, TaggedFileExt, WriteOptions, -}; +use lofty::tag::{Accessor, Tag, TagExt, TagType}; fn file_ref_save(path: &str, expected_file_type: FileType) { let path = format!("tests/taglib/data/{path}"); @@ -214,12 +213,13 @@ fn test_default_file_extensions() { // Marker test, Lofty does not replicate this API } +use lofty::properties::FileProperties; use rusty_fork::rusty_fork_test; rusty_fork_test! { #[test] fn test_file_resolver() { - lofty::apply_global_options(GlobalOptions::new().use_custom_resolvers(true)); + lofty::config::apply_global_options(GlobalOptions::new().use_custom_resolvers(true)); { let file = lofty::read_from_path("tests/taglib/data/xing.mp3").unwrap(); @@ -236,7 +236,7 @@ rusty_fork_test! { impl AudioFile for DummyResolver { type Properties = (); - fn read_from(_: &mut R, _: ParseOptions) -> lofty::Result + fn read_from(_: &mut R, _: ParseOptions) -> lofty::error::Result where R: Read + Seek, Self: Sized, @@ -244,7 +244,7 @@ rusty_fork_test! { Ok(Self) } - fn save_to(&self, _: &mut File, _: WriteOptions) -> lofty::Result<()> { + fn save_to(&self, _: &mut File, _: WriteOptions) -> lofty::error::Result<()> { unimplemented!() } diff --git a/tests/taglib/test_flac.rs b/tests/taglib/test_flac.rs index 8af0805c..e21ead32 100644 --- a/tests/taglib/test_flac.rs +++ b/tests/taglib/test_flac.rs @@ -3,13 +3,13 @@ use crate::util::get_file; use std::io::{Read, Seek, SeekFrom}; +use lofty::config::{ParseOptions, WriteOptions}; +use lofty::file::AudioFile; use lofty::flac::FlacFile; use lofty::id3::v2::Id3v2Tag; use lofty::ogg::{OggPictureStorage, VorbisComments}; -use lofty::{ - Accessor, AudioFile, MimeType, ParseOptions, Picture, PictureInformation, PictureType, TagExt, - WriteOptions, -}; +use lofty::picture::{MimeType, Picture, PictureInformation, PictureType}; +use lofty::tag::{Accessor, TagExt}; #[test] fn test_signature() { diff --git a/tests/taglib/test_flacpicture.rs b/tests/taglib/test_flacpicture.rs index 2480a544..981f55d4 100644 --- a/tests/taglib/test_flacpicture.rs +++ b/tests/taglib/test_flacpicture.rs @@ -1,4 +1,5 @@ -use lofty::{MimeType, ParsingMode, Picture, PictureType}; +use lofty::config::ParsingMode; +use lofty::picture::{MimeType, Picture, PictureType}; const DATA: &[u8] = &[ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x09, 0x69, 0x6D, 0x61, 0x67, 0x65, 0x2F, 0x70, 0x6E, diff --git a/tests/taglib/test_id3v2.rs b/tests/taglib/test_id3v2.rs index ad2ea51c..5b7dded9 100644 --- a/tests/taglib/test_id3v2.rs +++ b/tests/taglib/test_id3v2.rs @@ -4,6 +4,8 @@ use std::borrow::Cow; use std::collections::HashMap; use std::io::Seek; +use lofty::config::{ParseOptions, ParsingMode, WriteOptions}; +use lofty::file::AudioFile; use lofty::id3::v2::{ AttachedPictureFrame, ChannelInformation, ChannelType, CommentFrame, Event, EventTimingCodesFrame, EventType, ExtendedTextFrame, ExtendedUrlFrame, Frame, FrameFlags, @@ -13,10 +15,9 @@ use lofty::id3::v2::{ UrlLinkFrame, }; use lofty::mpeg::MpegFile; -use lofty::{ - Accessor, AudioFile, MimeType, ParseOptions, ParsingMode, Picture, PictureType, TagExt, - TextEncoding, WriteOptions, -}; +use lofty::picture::{MimeType, Picture, PictureType}; +use lofty::tag::{Accessor, TagExt}; +use lofty::TextEncoding; #[test] fn test_unsynch_decode() { diff --git a/tests/taglib/test_info.rs b/tests/taglib/test_info.rs index f1bce457..31231d75 100644 --- a/tests/taglib/test_info.rs +++ b/tests/taglib/test_info.rs @@ -1,5 +1,5 @@ use lofty::iff::wav::RIFFInfoList; -use lofty::Accessor; +use lofty::tag::Accessor; #[test] fn test_title() { diff --git a/tests/taglib/test_mp4.rs b/tests/taglib/test_mp4.rs index adc844c6..714ace31 100644 --- a/tests/taglib/test_mp4.rs +++ b/tests/taglib/test_mp4.rs @@ -4,11 +4,11 @@ use crate::util::get_file; use std::borrow::Cow; use std::io::{Read, Seek}; +use lofty::config::{ParseOptions, WriteOptions}; +use lofty::file::AudioFile; use lofty::mp4::{Atom, AtomData, AtomIdent, Ilst, Mp4Codec, Mp4File}; -use lofty::{ - Accessor, AudioFile, MimeType, ParseOptions, Picture, PictureType, TagExt, TagType, - WriteOptions, -}; +use lofty::picture::{MimeType, Picture, PictureType}; +use lofty::tag::{Accessor, TagExt, TagType}; #[test] fn test_properties_aac() { diff --git a/tests/taglib/test_mpc.rs b/tests/taglib/test_mpc.rs index 145e5361..32ba5889 100644 --- a/tests/taglib/test_mpc.rs +++ b/tests/taglib/test_mpc.rs @@ -4,9 +4,12 @@ use crate::util::get_file; use std::io::Seek; use lofty::ape::ApeTag; +use lofty::config::{ParseOptions, WriteOptions}; +use lofty::file::AudioFile; use lofty::id3::v1::Id3v1Tag; use lofty::musepack::{MpcFile, MpcProperties}; -use lofty::{Accessor, AudioFile, ParseOptions, Probe, TagExt, WriteOptions}; +use lofty::probe::Probe; +use lofty::tag::{Accessor, TagExt}; #[test] fn test_properties_sv8() { diff --git a/tests/taglib/test_mpeg.rs b/tests/taglib/test_mpeg.rs index 23173ba1..56bf709b 100644 --- a/tests/taglib/test_mpeg.rs +++ b/tests/taglib/test_mpeg.rs @@ -5,10 +5,12 @@ use std::fs::File; use std::io::Seek; use lofty::ape::ApeTag; +use lofty::config::{ParseOptions, WriteOptions}; +use lofty::file::AudioFile; use lofty::id3::v1::Id3v1Tag; use lofty::id3::v2::{Id3v2Tag, Id3v2Version}; use lofty::mpeg::MpegFile; -use lofty::{Accessor, AudioFile, ParseOptions, WriteOptions}; +use lofty::tag::Accessor; #[test] fn test_audio_properties_xing_header_cbr() { diff --git a/tests/taglib/test_ogaflac.rs b/tests/taglib/test_ogaflac.rs index ad749206..6f55d36b 100644 --- a/tests/taglib/test_ogaflac.rs +++ b/tests/taglib/test_ogaflac.rs @@ -2,9 +2,11 @@ use crate::temp_file; use std::io::{Seek, SeekFrom}; +use lofty::config::{ParseOptions, WriteOptions}; +use lofty::file::AudioFile; use lofty::flac::FlacFile; use lofty::ogg::VorbisComments; -use lofty::{Accessor, AudioFile, ParseOptions, WriteOptions}; +use lofty::tag::Accessor; // TODO: We don't support FLAC in OGA (#172) #[test] diff --git a/tests/taglib/test_ogg.rs b/tests/taglib/test_ogg.rs index 9111343e..43e5376d 100644 --- a/tests/taglib/test_ogg.rs +++ b/tests/taglib/test_ogg.rs @@ -4,8 +4,10 @@ use crate::util::get_file; use std::io::{Read, Seek, SeekFrom}; use byteorder::{LittleEndian, ReadBytesExt}; +use lofty::config::{ParseOptions, WriteOptions}; +use lofty::file::AudioFile; use lofty::ogg::VorbisFile; -use lofty::{Accessor, AudioFile, ParseOptions, WriteOptions}; +use lofty::tag::Accessor; #[test] fn test_simple() { diff --git a/tests/taglib/test_opus.rs b/tests/taglib/test_opus.rs index 71771ee8..6caac3ec 100644 --- a/tests/taglib/test_opus.rs +++ b/tests/taglib/test_opus.rs @@ -3,8 +3,10 @@ use crate::util::get_file; use std::io::Seek; +use lofty::config::{ParseOptions, WriteOptions}; +use lofty::file::AudioFile; use lofty::ogg::OpusFile; -use lofty::{Accessor, AudioFile, ParseOptions, WriteOptions}; +use lofty::tag::Accessor; #[test] fn test_audio_properties() { diff --git a/tests/taglib/test_speex.rs b/tests/taglib/test_speex.rs index 1c914c43..764a0a2b 100644 --- a/tests/taglib/test_speex.rs +++ b/tests/taglib/test_speex.rs @@ -1,11 +1,12 @@ use crate::temp_file; use crate::util::get_file; +use lofty::config::{ParseOptions, WriteOptions}; +use lofty::file::AudioFile; +use lofty::ogg::{SpeexFile, VorbisComments}; +use lofty::tag::Accessor; use std::io::Seek; -use lofty::ogg::{SpeexFile, VorbisComments}; -use lofty::{Accessor, AudioFile, ParseOptions, WriteOptions}; - #[test] fn test_audio_properties() { let f = get_file::("tests/taglib/data/empty.spx"); diff --git a/tests/taglib/test_wav.rs b/tests/taglib/test_wav.rs index e909ae20..dec415a8 100644 --- a/tests/taglib/test_wav.rs +++ b/tests/taglib/test_wav.rs @@ -1,8 +1,11 @@ use crate::temp_file; use crate::util::get_file; +use lofty::config::{ParseOptions, WriteOptions}; +use lofty::file::AudioFile; use lofty::id3::v2::Id3v2Tag; use lofty::iff::wav::{RIFFInfoList, WavFile, WavFormat}; -use lofty::{Accessor, AudioFile, ParseOptions, TagType, WriteOptions}; +use lofty::tag::{Accessor, TagType}; + use std::io::{Cursor, Read, Seek, SeekFrom}; #[test] diff --git a/tests/taglib/test_wavpack.rs b/tests/taglib/test_wavpack.rs index b0288a77..74d673dd 100644 --- a/tests/taglib/test_wavpack.rs +++ b/tests/taglib/test_wavpack.rs @@ -5,9 +5,11 @@ use std::fs::File; use std::io::Seek; use lofty::ape::ApeTag; +use lofty::config::{ParseOptions, WriteOptions}; +use lofty::file::AudioFile; use lofty::id3::v1::Id3v1Tag; +use lofty::tag::Accessor; use lofty::wavpack::WavPackFile; -use lofty::{Accessor, AudioFile, ParseOptions, WriteOptions}; #[test] fn test_no_length_properties() { diff --git a/tests/taglib/test_xiphcomment.rs b/tests/taglib/test_xiphcomment.rs index af46839f..9acb2ca9 100644 --- a/tests/taglib/test_xiphcomment.rs +++ b/tests/taglib/test_xiphcomment.rs @@ -2,11 +2,11 @@ use crate::temp_file; use std::io::Seek; +use lofty::config::{ParseOptions, WriteOptions}; +use lofty::file::AudioFile; use lofty::ogg::{OggPictureStorage, VorbisComments, VorbisFile}; -use lofty::{ - Accessor, AudioFile, MimeType, ParseOptions, Picture, PictureInformation, PictureType, TagExt, - WriteOptions, -}; +use lofty::picture::{MimeType, Picture, PictureInformation, PictureType}; +use lofty::tag::{Accessor, TagExt}; #[test] fn test_year() { diff --git a/tests/taglib/util/mod.rs b/tests/taglib/util/mod.rs index 81042ce4..78201d10 100644 --- a/tests/taglib/util/mod.rs +++ b/tests/taglib/util/mod.rs @@ -1,4 +1,5 @@ -use lofty::{AudioFile, ParseOptions}; +use lofty::config::ParseOptions; +use lofty::file::AudioFile; use std::fs::File; pub fn get_file(path: &str) -> F { From b054fb8ad6d0d27f909d0f1c85e2c61e2a8c1b14 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Tue, 23 Apr 2024 22:10:36 -0400 Subject: [PATCH 59/75] Tests: Update TagLib tests for recent changes --- lofty/Cargo.toml | 1 + {tests => lofty/tests}/taglib/data/005411.id3 | Bin {tests => lofty/tests}/taglib/data/alaw.aifc | Bin {tests => lofty/tests}/taglib/data/alaw.wav | Bin {tests => lofty/tests}/taglib/data/bladeenc.mp3 | Bin {tests => lofty/tests}/taglib/data/blank_video.m4v | Bin {tests => lofty/tests}/taglib/data/broken-tenc.id3 | Bin {tests => lofty/tests}/taglib/data/click.mpc | Bin {tests => lofty/tests}/taglib/data/click.wv | Bin .../tests}/taglib/data/compressed_id3_frame.mp3 | Bin .../taglib/data/correctness_gain_silent_output.opus | Bin {tests => lofty/tests}/taglib/data/covr-junk.m4a | Bin {tests => lofty/tests}/taglib/data/dsd_stereo.wv | Bin .../tests}/taglib/data/duplicate_id3v2.aiff | Bin .../tests}/taglib/data/duplicate_id3v2.mp3 | Bin .../tests}/taglib/data/duplicate_tags.wav | Bin .../tests}/taglib/data/empty-seektable.flac | Bin {tests => lofty/tests}/taglib/data/empty.aiff | Bin {tests => lofty/tests}/taglib/data/empty.ogg | Bin {tests => lofty/tests}/taglib/data/empty.spx | Bin {tests => lofty/tests}/taglib/data/empty.wav | Bin {tests => lofty/tests}/taglib/data/empty_alac.m4a | Bin {tests => lofty/tests}/taglib/data/empty_flac.oga | Bin {tests => lofty/tests}/taglib/data/empty_vorbis.oga | Bin .../tests}/taglib/data/excessive_alloc.aif | Bin .../tests}/taglib/data/excessive_alloc.mp3 | Bin {tests => lofty/tests}/taglib/data/float64.wav | Bin {tests => lofty/tests}/taglib/data/garbage.mp3 | Bin {tests => lofty/tests}/taglib/data/gnre.m4a | Bin {tests => lofty/tests}/taglib/data/has-tags.m4a | Bin {tests => lofty/tests}/taglib/data/id3v22-tda.mp3 | Bin {tests => lofty/tests}/taglib/data/ilst-is-last.m4a | Bin {tests => lofty/tests}/taglib/data/infloop.m4a | Bin {tests => lofty/tests}/taglib/data/infloop.mpc | Bin {tests => lofty/tests}/taglib/data/infloop.wav | Bin {tests => lofty/tests}/taglib/data/infloop.wv | Bin .../tests}/taglib/data/invalid-frames1.mp3 | Bin .../tests}/taglib/data/invalid-frames2.mp3 | Bin .../tests}/taglib/data/invalid-frames3.mp3 | Bin {tests => lofty/tests}/taglib/data/lame_cbr.mp3 | Bin {tests => lofty/tests}/taglib/data/lame_vbr.mp3 | Bin {tests => lofty/tests}/taglib/data/longloop.ape | Bin .../tests}/taglib/data/lowercase-fields.ogg | Bin {tests => lofty/tests}/taglib/data/mac-390-hdr.ape | Bin {tests => lofty/tests}/taglib/data/mac-396.ape | Bin .../tests}/taglib/data/mac-399-id3v2.ape | Bin .../tests}/taglib/data/mac-399-tagged.ape | Bin {tests => lofty/tests}/taglib/data/mac-399.ape | Bin {tests => lofty/tests}/taglib/data/mpeg2.mp3 | Bin {tests => lofty/tests}/taglib/data/multiple-vc.flac | Bin {tests => lofty/tests}/taglib/data/no-extension | Bin {tests => lofty/tests}/taglib/data/no-tags.3g2 | Bin {tests => lofty/tests}/taglib/data/no-tags.ape | Bin {tests => lofty/tests}/taglib/data/no-tags.flac | Bin {tests => lofty/tests}/taglib/data/no-tags.m4a | Bin {tests => lofty/tests}/taglib/data/no-tags.mpc | Bin {tests => lofty/tests}/taglib/data/no_length.wv | Bin .../tests}/taglib/data/non-full-meta.m4a | Bin .../tests}/taglib/data/non_standard_rate.wv | Bin .../tests}/taglib/data/pcm_with_fact_chunk.wav | Bin {tests => lofty/tests}/taglib/data/rare_frames.mp3 | Bin {tests => lofty/tests}/taglib/data/segfault.aif | Bin {tests => lofty/tests}/taglib/data/segfault.mpc | Bin {tests => lofty/tests}/taglib/data/segfault.oga | Bin {tests => lofty/tests}/taglib/data/segfault.wav | Bin {tests => lofty/tests}/taglib/data/segfault2.mpc | 0 {tests => lofty/tests}/taglib/data/silence-1.wma | Bin .../tests}/taglib/data/silence-44-s.flac | Bin {tests => lofty/tests}/taglib/data/sinewave.flac | Bin {tests => lofty/tests}/taglib/data/sv4_header.mpc | Bin {tests => lofty/tests}/taglib/data/sv5_header.mpc | Bin {tests => lofty/tests}/taglib/data/sv8_header.mpc | Bin {tests => lofty/tests}/taglib/data/tagged.wv | Bin {tests => lofty/tests}/taglib/data/uint8we.wav | Bin .../tests}/taglib/data/unsupported-extension.xx | Bin {tests => lofty/tests}/taglib/data/unsynch.id3 | Bin {tests => lofty/tests}/taglib/data/w000.mp3 | Bin {tests => lofty/tests}/taglib/data/xing.mp3 | Bin .../tests}/taglib/data/zero-length-mdat.m4a | Bin .../tests}/taglib/data/zero-size-chunk.wav | Bin .../tests}/taglib/data/zero-sized-padding.flac | Bin {tests => lofty/tests}/taglib/data/zerodiv.ape | Bin {tests => lofty/tests}/taglib/data/zerodiv.mpc | Bin {tests => lofty/tests}/taglib/main.rs | 0 {tests => lofty/tests}/taglib/test_aiff.rs | 0 {tests => lofty/tests}/taglib/test_ape.rs | 0 {tests => lofty/tests}/taglib/test_apetag.rs | 0 {tests => lofty/tests}/taglib/test_fileref.rs | 11 ++++++++--- {tests => lofty/tests}/taglib/test_flac.rs | 0 {tests => lofty/tests}/taglib/test_flacpicture.rs | 0 {tests => lofty/tests}/taglib/test_id3v1.rs | 0 {tests => lofty/tests}/taglib/test_id3v2.rs | 0 {tests => lofty/tests}/taglib/test_info.rs | 6 +++--- {tests => lofty/tests}/taglib/test_mp4.rs | 0 {tests => lofty/tests}/taglib/test_mpc.rs | 0 {tests => lofty/tests}/taglib/test_mpeg.rs | 0 {tests => lofty/tests}/taglib/test_ogaflac.rs | 0 {tests => lofty/tests}/taglib/test_ogg.rs | 0 {tests => lofty/tests}/taglib/test_opus.rs | 0 {tests => lofty/tests}/taglib/test_speex.rs | 0 {tests => lofty/tests}/taglib/test_synchdata.rs | 0 {tests => lofty/tests}/taglib/test_wav.rs | 10 +++++----- {tests => lofty/tests}/taglib/test_wavpack.rs | 0 {tests => lofty/tests}/taglib/test_xiphcomment.rs | 0 {tests => lofty/tests}/taglib/util/mod.rs | 0 105 files changed, 17 insertions(+), 11 deletions(-) rename {tests => lofty/tests}/taglib/data/005411.id3 (100%) rename {tests => lofty/tests}/taglib/data/alaw.aifc (100%) rename {tests => lofty/tests}/taglib/data/alaw.wav (100%) rename {tests => lofty/tests}/taglib/data/bladeenc.mp3 (100%) rename {tests => lofty/tests}/taglib/data/blank_video.m4v (100%) rename {tests => lofty/tests}/taglib/data/broken-tenc.id3 (100%) rename {tests => lofty/tests}/taglib/data/click.mpc (100%) rename {tests => lofty/tests}/taglib/data/click.wv (100%) rename {tests => lofty/tests}/taglib/data/compressed_id3_frame.mp3 (100%) rename {tests => lofty/tests}/taglib/data/correctness_gain_silent_output.opus (100%) rename {tests => lofty/tests}/taglib/data/covr-junk.m4a (100%) rename {tests => lofty/tests}/taglib/data/dsd_stereo.wv (100%) rename {tests => lofty/tests}/taglib/data/duplicate_id3v2.aiff (100%) rename {tests => lofty/tests}/taglib/data/duplicate_id3v2.mp3 (100%) rename {tests => lofty/tests}/taglib/data/duplicate_tags.wav (100%) rename {tests => lofty/tests}/taglib/data/empty-seektable.flac (100%) rename {tests => lofty/tests}/taglib/data/empty.aiff (100%) rename {tests => lofty/tests}/taglib/data/empty.ogg (100%) rename {tests => lofty/tests}/taglib/data/empty.spx (100%) rename {tests => lofty/tests}/taglib/data/empty.wav (100%) rename {tests => lofty/tests}/taglib/data/empty_alac.m4a (100%) rename {tests => lofty/tests}/taglib/data/empty_flac.oga (100%) rename {tests => lofty/tests}/taglib/data/empty_vorbis.oga (100%) rename {tests => lofty/tests}/taglib/data/excessive_alloc.aif (100%) rename {tests => lofty/tests}/taglib/data/excessive_alloc.mp3 (100%) rename {tests => lofty/tests}/taglib/data/float64.wav (100%) rename {tests => lofty/tests}/taglib/data/garbage.mp3 (100%) rename {tests => lofty/tests}/taglib/data/gnre.m4a (100%) rename {tests => lofty/tests}/taglib/data/has-tags.m4a (100%) rename {tests => lofty/tests}/taglib/data/id3v22-tda.mp3 (100%) rename {tests => lofty/tests}/taglib/data/ilst-is-last.m4a (100%) rename {tests => lofty/tests}/taglib/data/infloop.m4a (100%) rename {tests => lofty/tests}/taglib/data/infloop.mpc (100%) rename {tests => lofty/tests}/taglib/data/infloop.wav (100%) rename {tests => lofty/tests}/taglib/data/infloop.wv (100%) rename {tests => lofty/tests}/taglib/data/invalid-frames1.mp3 (100%) rename {tests => lofty/tests}/taglib/data/invalid-frames2.mp3 (100%) rename {tests => lofty/tests}/taglib/data/invalid-frames3.mp3 (100%) rename {tests => lofty/tests}/taglib/data/lame_cbr.mp3 (100%) rename {tests => lofty/tests}/taglib/data/lame_vbr.mp3 (100%) rename {tests => lofty/tests}/taglib/data/longloop.ape (100%) rename {tests => lofty/tests}/taglib/data/lowercase-fields.ogg (100%) rename {tests => lofty/tests}/taglib/data/mac-390-hdr.ape (100%) rename {tests => lofty/tests}/taglib/data/mac-396.ape (100%) rename {tests => lofty/tests}/taglib/data/mac-399-id3v2.ape (100%) rename {tests => lofty/tests}/taglib/data/mac-399-tagged.ape (100%) rename {tests => lofty/tests}/taglib/data/mac-399.ape (100%) rename {tests => lofty/tests}/taglib/data/mpeg2.mp3 (100%) rename {tests => lofty/tests}/taglib/data/multiple-vc.flac (100%) rename {tests => lofty/tests}/taglib/data/no-extension (100%) rename {tests => lofty/tests}/taglib/data/no-tags.3g2 (100%) rename {tests => lofty/tests}/taglib/data/no-tags.ape (100%) rename {tests => lofty/tests}/taglib/data/no-tags.flac (100%) rename {tests => lofty/tests}/taglib/data/no-tags.m4a (100%) rename {tests => lofty/tests}/taglib/data/no-tags.mpc (100%) rename {tests => lofty/tests}/taglib/data/no_length.wv (100%) rename {tests => lofty/tests}/taglib/data/non-full-meta.m4a (100%) rename {tests => lofty/tests}/taglib/data/non_standard_rate.wv (100%) rename {tests => lofty/tests}/taglib/data/pcm_with_fact_chunk.wav (100%) rename {tests => lofty/tests}/taglib/data/rare_frames.mp3 (100%) rename {tests => lofty/tests}/taglib/data/segfault.aif (100%) rename {tests => lofty/tests}/taglib/data/segfault.mpc (100%) rename {tests => lofty/tests}/taglib/data/segfault.oga (100%) rename {tests => lofty/tests}/taglib/data/segfault.wav (100%) rename {tests => lofty/tests}/taglib/data/segfault2.mpc (100%) rename {tests => lofty/tests}/taglib/data/silence-1.wma (100%) rename {tests => lofty/tests}/taglib/data/silence-44-s.flac (100%) rename {tests => lofty/tests}/taglib/data/sinewave.flac (100%) rename {tests => lofty/tests}/taglib/data/sv4_header.mpc (100%) rename {tests => lofty/tests}/taglib/data/sv5_header.mpc (100%) rename {tests => lofty/tests}/taglib/data/sv8_header.mpc (100%) rename {tests => lofty/tests}/taglib/data/tagged.wv (100%) rename {tests => lofty/tests}/taglib/data/uint8we.wav (100%) rename {tests => lofty/tests}/taglib/data/unsupported-extension.xx (100%) rename {tests => lofty/tests}/taglib/data/unsynch.id3 (100%) rename {tests => lofty/tests}/taglib/data/w000.mp3 (100%) rename {tests => lofty/tests}/taglib/data/xing.mp3 (100%) rename {tests => lofty/tests}/taglib/data/zero-length-mdat.m4a (100%) rename {tests => lofty/tests}/taglib/data/zero-size-chunk.wav (100%) rename {tests => lofty/tests}/taglib/data/zero-sized-padding.flac (100%) rename {tests => lofty/tests}/taglib/data/zerodiv.ape (100%) rename {tests => lofty/tests}/taglib/data/zerodiv.mpc (100%) rename {tests => lofty/tests}/taglib/main.rs (100%) rename {tests => lofty/tests}/taglib/test_aiff.rs (100%) rename {tests => lofty/tests}/taglib/test_ape.rs (100%) rename {tests => lofty/tests}/taglib/test_apetag.rs (100%) rename {tests => lofty/tests}/taglib/test_fileref.rs (96%) rename {tests => lofty/tests}/taglib/test_flac.rs (100%) rename {tests => lofty/tests}/taglib/test_flacpicture.rs (100%) rename {tests => lofty/tests}/taglib/test_id3v1.rs (100%) rename {tests => lofty/tests}/taglib/test_id3v2.rs (100%) rename {tests => lofty/tests}/taglib/test_info.rs (85%) rename {tests => lofty/tests}/taglib/test_mp4.rs (100%) rename {tests => lofty/tests}/taglib/test_mpc.rs (100%) rename {tests => lofty/tests}/taglib/test_mpeg.rs (100%) rename {tests => lofty/tests}/taglib/test_ogaflac.rs (100%) rename {tests => lofty/tests}/taglib/test_ogg.rs (100%) rename {tests => lofty/tests}/taglib/test_opus.rs (100%) rename {tests => lofty/tests}/taglib/test_speex.rs (100%) rename {tests => lofty/tests}/taglib/test_synchdata.rs (100%) rename {tests => lofty/tests}/taglib/test_wav.rs (97%) rename {tests => lofty/tests}/taglib/test_wavpack.rs (100%) rename {tests => lofty/tests}/taglib/test_xiphcomment.rs (100%) rename {tests => lofty/tests}/taglib/util/mod.rs (100%) diff --git a/lofty/Cargo.toml b/lofty/Cargo.toml index 8551b194..4703a4ce 100644 --- a/lofty/Cargo.toml +++ b/lofty/Cargo.toml @@ -34,6 +34,7 @@ id3v2_compression_support = ["dep:flate2"] [dev-dependencies] # WAV properties validity tests hound = { git = "https://github.com/ruuda/hound.git", rev = "02e66effb33683dd6acb92df792683ee46ad6a59" } +rusty-fork = "0.3.0" # tag_writer example structopt = { version = "0.3.26", default-features = false } tempfile = "3.15.0" diff --git a/tests/taglib/data/005411.id3 b/lofty/tests/taglib/data/005411.id3 similarity index 100% rename from tests/taglib/data/005411.id3 rename to lofty/tests/taglib/data/005411.id3 diff --git a/tests/taglib/data/alaw.aifc b/lofty/tests/taglib/data/alaw.aifc similarity index 100% rename from tests/taglib/data/alaw.aifc rename to lofty/tests/taglib/data/alaw.aifc diff --git a/tests/taglib/data/alaw.wav b/lofty/tests/taglib/data/alaw.wav similarity index 100% rename from tests/taglib/data/alaw.wav rename to lofty/tests/taglib/data/alaw.wav diff --git a/tests/taglib/data/bladeenc.mp3 b/lofty/tests/taglib/data/bladeenc.mp3 similarity index 100% rename from tests/taglib/data/bladeenc.mp3 rename to lofty/tests/taglib/data/bladeenc.mp3 diff --git a/tests/taglib/data/blank_video.m4v b/lofty/tests/taglib/data/blank_video.m4v similarity index 100% rename from tests/taglib/data/blank_video.m4v rename to lofty/tests/taglib/data/blank_video.m4v diff --git a/tests/taglib/data/broken-tenc.id3 b/lofty/tests/taglib/data/broken-tenc.id3 similarity index 100% rename from tests/taglib/data/broken-tenc.id3 rename to lofty/tests/taglib/data/broken-tenc.id3 diff --git a/tests/taglib/data/click.mpc b/lofty/tests/taglib/data/click.mpc similarity index 100% rename from tests/taglib/data/click.mpc rename to lofty/tests/taglib/data/click.mpc diff --git a/tests/taglib/data/click.wv b/lofty/tests/taglib/data/click.wv similarity index 100% rename from tests/taglib/data/click.wv rename to lofty/tests/taglib/data/click.wv diff --git a/tests/taglib/data/compressed_id3_frame.mp3 b/lofty/tests/taglib/data/compressed_id3_frame.mp3 similarity index 100% rename from tests/taglib/data/compressed_id3_frame.mp3 rename to lofty/tests/taglib/data/compressed_id3_frame.mp3 diff --git a/tests/taglib/data/correctness_gain_silent_output.opus b/lofty/tests/taglib/data/correctness_gain_silent_output.opus similarity index 100% rename from tests/taglib/data/correctness_gain_silent_output.opus rename to lofty/tests/taglib/data/correctness_gain_silent_output.opus diff --git a/tests/taglib/data/covr-junk.m4a b/lofty/tests/taglib/data/covr-junk.m4a similarity index 100% rename from tests/taglib/data/covr-junk.m4a rename to lofty/tests/taglib/data/covr-junk.m4a diff --git a/tests/taglib/data/dsd_stereo.wv b/lofty/tests/taglib/data/dsd_stereo.wv similarity index 100% rename from tests/taglib/data/dsd_stereo.wv rename to lofty/tests/taglib/data/dsd_stereo.wv diff --git a/tests/taglib/data/duplicate_id3v2.aiff b/lofty/tests/taglib/data/duplicate_id3v2.aiff similarity index 100% rename from tests/taglib/data/duplicate_id3v2.aiff rename to lofty/tests/taglib/data/duplicate_id3v2.aiff diff --git a/tests/taglib/data/duplicate_id3v2.mp3 b/lofty/tests/taglib/data/duplicate_id3v2.mp3 similarity index 100% rename from tests/taglib/data/duplicate_id3v2.mp3 rename to lofty/tests/taglib/data/duplicate_id3v2.mp3 diff --git a/tests/taglib/data/duplicate_tags.wav b/lofty/tests/taglib/data/duplicate_tags.wav similarity index 100% rename from tests/taglib/data/duplicate_tags.wav rename to lofty/tests/taglib/data/duplicate_tags.wav diff --git a/tests/taglib/data/empty-seektable.flac b/lofty/tests/taglib/data/empty-seektable.flac similarity index 100% rename from tests/taglib/data/empty-seektable.flac rename to lofty/tests/taglib/data/empty-seektable.flac diff --git a/tests/taglib/data/empty.aiff b/lofty/tests/taglib/data/empty.aiff similarity index 100% rename from tests/taglib/data/empty.aiff rename to lofty/tests/taglib/data/empty.aiff diff --git a/tests/taglib/data/empty.ogg b/lofty/tests/taglib/data/empty.ogg similarity index 100% rename from tests/taglib/data/empty.ogg rename to lofty/tests/taglib/data/empty.ogg diff --git a/tests/taglib/data/empty.spx b/lofty/tests/taglib/data/empty.spx similarity index 100% rename from tests/taglib/data/empty.spx rename to lofty/tests/taglib/data/empty.spx diff --git a/tests/taglib/data/empty.wav b/lofty/tests/taglib/data/empty.wav similarity index 100% rename from tests/taglib/data/empty.wav rename to lofty/tests/taglib/data/empty.wav diff --git a/tests/taglib/data/empty_alac.m4a b/lofty/tests/taglib/data/empty_alac.m4a similarity index 100% rename from tests/taglib/data/empty_alac.m4a rename to lofty/tests/taglib/data/empty_alac.m4a diff --git a/tests/taglib/data/empty_flac.oga b/lofty/tests/taglib/data/empty_flac.oga similarity index 100% rename from tests/taglib/data/empty_flac.oga rename to lofty/tests/taglib/data/empty_flac.oga diff --git a/tests/taglib/data/empty_vorbis.oga b/lofty/tests/taglib/data/empty_vorbis.oga similarity index 100% rename from tests/taglib/data/empty_vorbis.oga rename to lofty/tests/taglib/data/empty_vorbis.oga diff --git a/tests/taglib/data/excessive_alloc.aif b/lofty/tests/taglib/data/excessive_alloc.aif similarity index 100% rename from tests/taglib/data/excessive_alloc.aif rename to lofty/tests/taglib/data/excessive_alloc.aif diff --git a/tests/taglib/data/excessive_alloc.mp3 b/lofty/tests/taglib/data/excessive_alloc.mp3 similarity index 100% rename from tests/taglib/data/excessive_alloc.mp3 rename to lofty/tests/taglib/data/excessive_alloc.mp3 diff --git a/tests/taglib/data/float64.wav b/lofty/tests/taglib/data/float64.wav similarity index 100% rename from tests/taglib/data/float64.wav rename to lofty/tests/taglib/data/float64.wav diff --git a/tests/taglib/data/garbage.mp3 b/lofty/tests/taglib/data/garbage.mp3 similarity index 100% rename from tests/taglib/data/garbage.mp3 rename to lofty/tests/taglib/data/garbage.mp3 diff --git a/tests/taglib/data/gnre.m4a b/lofty/tests/taglib/data/gnre.m4a similarity index 100% rename from tests/taglib/data/gnre.m4a rename to lofty/tests/taglib/data/gnre.m4a diff --git a/tests/taglib/data/has-tags.m4a b/lofty/tests/taglib/data/has-tags.m4a similarity index 100% rename from tests/taglib/data/has-tags.m4a rename to lofty/tests/taglib/data/has-tags.m4a diff --git a/tests/taglib/data/id3v22-tda.mp3 b/lofty/tests/taglib/data/id3v22-tda.mp3 similarity index 100% rename from tests/taglib/data/id3v22-tda.mp3 rename to lofty/tests/taglib/data/id3v22-tda.mp3 diff --git a/tests/taglib/data/ilst-is-last.m4a b/lofty/tests/taglib/data/ilst-is-last.m4a similarity index 100% rename from tests/taglib/data/ilst-is-last.m4a rename to lofty/tests/taglib/data/ilst-is-last.m4a diff --git a/tests/taglib/data/infloop.m4a b/lofty/tests/taglib/data/infloop.m4a similarity index 100% rename from tests/taglib/data/infloop.m4a rename to lofty/tests/taglib/data/infloop.m4a diff --git a/tests/taglib/data/infloop.mpc b/lofty/tests/taglib/data/infloop.mpc similarity index 100% rename from tests/taglib/data/infloop.mpc rename to lofty/tests/taglib/data/infloop.mpc diff --git a/tests/taglib/data/infloop.wav b/lofty/tests/taglib/data/infloop.wav similarity index 100% rename from tests/taglib/data/infloop.wav rename to lofty/tests/taglib/data/infloop.wav diff --git a/tests/taglib/data/infloop.wv b/lofty/tests/taglib/data/infloop.wv similarity index 100% rename from tests/taglib/data/infloop.wv rename to lofty/tests/taglib/data/infloop.wv diff --git a/tests/taglib/data/invalid-frames1.mp3 b/lofty/tests/taglib/data/invalid-frames1.mp3 similarity index 100% rename from tests/taglib/data/invalid-frames1.mp3 rename to lofty/tests/taglib/data/invalid-frames1.mp3 diff --git a/tests/taglib/data/invalid-frames2.mp3 b/lofty/tests/taglib/data/invalid-frames2.mp3 similarity index 100% rename from tests/taglib/data/invalid-frames2.mp3 rename to lofty/tests/taglib/data/invalid-frames2.mp3 diff --git a/tests/taglib/data/invalid-frames3.mp3 b/lofty/tests/taglib/data/invalid-frames3.mp3 similarity index 100% rename from tests/taglib/data/invalid-frames3.mp3 rename to lofty/tests/taglib/data/invalid-frames3.mp3 diff --git a/tests/taglib/data/lame_cbr.mp3 b/lofty/tests/taglib/data/lame_cbr.mp3 similarity index 100% rename from tests/taglib/data/lame_cbr.mp3 rename to lofty/tests/taglib/data/lame_cbr.mp3 diff --git a/tests/taglib/data/lame_vbr.mp3 b/lofty/tests/taglib/data/lame_vbr.mp3 similarity index 100% rename from tests/taglib/data/lame_vbr.mp3 rename to lofty/tests/taglib/data/lame_vbr.mp3 diff --git a/tests/taglib/data/longloop.ape b/lofty/tests/taglib/data/longloop.ape similarity index 100% rename from tests/taglib/data/longloop.ape rename to lofty/tests/taglib/data/longloop.ape diff --git a/tests/taglib/data/lowercase-fields.ogg b/lofty/tests/taglib/data/lowercase-fields.ogg similarity index 100% rename from tests/taglib/data/lowercase-fields.ogg rename to lofty/tests/taglib/data/lowercase-fields.ogg diff --git a/tests/taglib/data/mac-390-hdr.ape b/lofty/tests/taglib/data/mac-390-hdr.ape similarity index 100% rename from tests/taglib/data/mac-390-hdr.ape rename to lofty/tests/taglib/data/mac-390-hdr.ape diff --git a/tests/taglib/data/mac-396.ape b/lofty/tests/taglib/data/mac-396.ape similarity index 100% rename from tests/taglib/data/mac-396.ape rename to lofty/tests/taglib/data/mac-396.ape diff --git a/tests/taglib/data/mac-399-id3v2.ape b/lofty/tests/taglib/data/mac-399-id3v2.ape similarity index 100% rename from tests/taglib/data/mac-399-id3v2.ape rename to lofty/tests/taglib/data/mac-399-id3v2.ape diff --git a/tests/taglib/data/mac-399-tagged.ape b/lofty/tests/taglib/data/mac-399-tagged.ape similarity index 100% rename from tests/taglib/data/mac-399-tagged.ape rename to lofty/tests/taglib/data/mac-399-tagged.ape diff --git a/tests/taglib/data/mac-399.ape b/lofty/tests/taglib/data/mac-399.ape similarity index 100% rename from tests/taglib/data/mac-399.ape rename to lofty/tests/taglib/data/mac-399.ape diff --git a/tests/taglib/data/mpeg2.mp3 b/lofty/tests/taglib/data/mpeg2.mp3 similarity index 100% rename from tests/taglib/data/mpeg2.mp3 rename to lofty/tests/taglib/data/mpeg2.mp3 diff --git a/tests/taglib/data/multiple-vc.flac b/lofty/tests/taglib/data/multiple-vc.flac similarity index 100% rename from tests/taglib/data/multiple-vc.flac rename to lofty/tests/taglib/data/multiple-vc.flac diff --git a/tests/taglib/data/no-extension b/lofty/tests/taglib/data/no-extension similarity index 100% rename from tests/taglib/data/no-extension rename to lofty/tests/taglib/data/no-extension diff --git a/tests/taglib/data/no-tags.3g2 b/lofty/tests/taglib/data/no-tags.3g2 similarity index 100% rename from tests/taglib/data/no-tags.3g2 rename to lofty/tests/taglib/data/no-tags.3g2 diff --git a/tests/taglib/data/no-tags.ape b/lofty/tests/taglib/data/no-tags.ape similarity index 100% rename from tests/taglib/data/no-tags.ape rename to lofty/tests/taglib/data/no-tags.ape diff --git a/tests/taglib/data/no-tags.flac b/lofty/tests/taglib/data/no-tags.flac similarity index 100% rename from tests/taglib/data/no-tags.flac rename to lofty/tests/taglib/data/no-tags.flac diff --git a/tests/taglib/data/no-tags.m4a b/lofty/tests/taglib/data/no-tags.m4a similarity index 100% rename from tests/taglib/data/no-tags.m4a rename to lofty/tests/taglib/data/no-tags.m4a diff --git a/tests/taglib/data/no-tags.mpc b/lofty/tests/taglib/data/no-tags.mpc similarity index 100% rename from tests/taglib/data/no-tags.mpc rename to lofty/tests/taglib/data/no-tags.mpc diff --git a/tests/taglib/data/no_length.wv b/lofty/tests/taglib/data/no_length.wv similarity index 100% rename from tests/taglib/data/no_length.wv rename to lofty/tests/taglib/data/no_length.wv diff --git a/tests/taglib/data/non-full-meta.m4a b/lofty/tests/taglib/data/non-full-meta.m4a similarity index 100% rename from tests/taglib/data/non-full-meta.m4a rename to lofty/tests/taglib/data/non-full-meta.m4a diff --git a/tests/taglib/data/non_standard_rate.wv b/lofty/tests/taglib/data/non_standard_rate.wv similarity index 100% rename from tests/taglib/data/non_standard_rate.wv rename to lofty/tests/taglib/data/non_standard_rate.wv diff --git a/tests/taglib/data/pcm_with_fact_chunk.wav b/lofty/tests/taglib/data/pcm_with_fact_chunk.wav similarity index 100% rename from tests/taglib/data/pcm_with_fact_chunk.wav rename to lofty/tests/taglib/data/pcm_with_fact_chunk.wav diff --git a/tests/taglib/data/rare_frames.mp3 b/lofty/tests/taglib/data/rare_frames.mp3 similarity index 100% rename from tests/taglib/data/rare_frames.mp3 rename to lofty/tests/taglib/data/rare_frames.mp3 diff --git a/tests/taglib/data/segfault.aif b/lofty/tests/taglib/data/segfault.aif similarity index 100% rename from tests/taglib/data/segfault.aif rename to lofty/tests/taglib/data/segfault.aif diff --git a/tests/taglib/data/segfault.mpc b/lofty/tests/taglib/data/segfault.mpc similarity index 100% rename from tests/taglib/data/segfault.mpc rename to lofty/tests/taglib/data/segfault.mpc diff --git a/tests/taglib/data/segfault.oga b/lofty/tests/taglib/data/segfault.oga similarity index 100% rename from tests/taglib/data/segfault.oga rename to lofty/tests/taglib/data/segfault.oga diff --git a/tests/taglib/data/segfault.wav b/lofty/tests/taglib/data/segfault.wav similarity index 100% rename from tests/taglib/data/segfault.wav rename to lofty/tests/taglib/data/segfault.wav diff --git a/tests/taglib/data/segfault2.mpc b/lofty/tests/taglib/data/segfault2.mpc similarity index 100% rename from tests/taglib/data/segfault2.mpc rename to lofty/tests/taglib/data/segfault2.mpc diff --git a/tests/taglib/data/silence-1.wma b/lofty/tests/taglib/data/silence-1.wma similarity index 100% rename from tests/taglib/data/silence-1.wma rename to lofty/tests/taglib/data/silence-1.wma diff --git a/tests/taglib/data/silence-44-s.flac b/lofty/tests/taglib/data/silence-44-s.flac similarity index 100% rename from tests/taglib/data/silence-44-s.flac rename to lofty/tests/taglib/data/silence-44-s.flac diff --git a/tests/taglib/data/sinewave.flac b/lofty/tests/taglib/data/sinewave.flac similarity index 100% rename from tests/taglib/data/sinewave.flac rename to lofty/tests/taglib/data/sinewave.flac diff --git a/tests/taglib/data/sv4_header.mpc b/lofty/tests/taglib/data/sv4_header.mpc similarity index 100% rename from tests/taglib/data/sv4_header.mpc rename to lofty/tests/taglib/data/sv4_header.mpc diff --git a/tests/taglib/data/sv5_header.mpc b/lofty/tests/taglib/data/sv5_header.mpc similarity index 100% rename from tests/taglib/data/sv5_header.mpc rename to lofty/tests/taglib/data/sv5_header.mpc diff --git a/tests/taglib/data/sv8_header.mpc b/lofty/tests/taglib/data/sv8_header.mpc similarity index 100% rename from tests/taglib/data/sv8_header.mpc rename to lofty/tests/taglib/data/sv8_header.mpc diff --git a/tests/taglib/data/tagged.wv b/lofty/tests/taglib/data/tagged.wv similarity index 100% rename from tests/taglib/data/tagged.wv rename to lofty/tests/taglib/data/tagged.wv diff --git a/tests/taglib/data/uint8we.wav b/lofty/tests/taglib/data/uint8we.wav similarity index 100% rename from tests/taglib/data/uint8we.wav rename to lofty/tests/taglib/data/uint8we.wav diff --git a/tests/taglib/data/unsupported-extension.xx b/lofty/tests/taglib/data/unsupported-extension.xx similarity index 100% rename from tests/taglib/data/unsupported-extension.xx rename to lofty/tests/taglib/data/unsupported-extension.xx diff --git a/tests/taglib/data/unsynch.id3 b/lofty/tests/taglib/data/unsynch.id3 similarity index 100% rename from tests/taglib/data/unsynch.id3 rename to lofty/tests/taglib/data/unsynch.id3 diff --git a/tests/taglib/data/w000.mp3 b/lofty/tests/taglib/data/w000.mp3 similarity index 100% rename from tests/taglib/data/w000.mp3 rename to lofty/tests/taglib/data/w000.mp3 diff --git a/tests/taglib/data/xing.mp3 b/lofty/tests/taglib/data/xing.mp3 similarity index 100% rename from tests/taglib/data/xing.mp3 rename to lofty/tests/taglib/data/xing.mp3 diff --git a/tests/taglib/data/zero-length-mdat.m4a b/lofty/tests/taglib/data/zero-length-mdat.m4a similarity index 100% rename from tests/taglib/data/zero-length-mdat.m4a rename to lofty/tests/taglib/data/zero-length-mdat.m4a diff --git a/tests/taglib/data/zero-size-chunk.wav b/lofty/tests/taglib/data/zero-size-chunk.wav similarity index 100% rename from tests/taglib/data/zero-size-chunk.wav rename to lofty/tests/taglib/data/zero-size-chunk.wav diff --git a/tests/taglib/data/zero-sized-padding.flac b/lofty/tests/taglib/data/zero-sized-padding.flac similarity index 100% rename from tests/taglib/data/zero-sized-padding.flac rename to lofty/tests/taglib/data/zero-sized-padding.flac diff --git a/tests/taglib/data/zerodiv.ape b/lofty/tests/taglib/data/zerodiv.ape similarity index 100% rename from tests/taglib/data/zerodiv.ape rename to lofty/tests/taglib/data/zerodiv.ape diff --git a/tests/taglib/data/zerodiv.mpc b/lofty/tests/taglib/data/zerodiv.mpc similarity index 100% rename from tests/taglib/data/zerodiv.mpc rename to lofty/tests/taglib/data/zerodiv.mpc diff --git a/tests/taglib/main.rs b/lofty/tests/taglib/main.rs similarity index 100% rename from tests/taglib/main.rs rename to lofty/tests/taglib/main.rs diff --git a/tests/taglib/test_aiff.rs b/lofty/tests/taglib/test_aiff.rs similarity index 100% rename from tests/taglib/test_aiff.rs rename to lofty/tests/taglib/test_aiff.rs diff --git a/tests/taglib/test_ape.rs b/lofty/tests/taglib/test_ape.rs similarity index 100% rename from tests/taglib/test_ape.rs rename to lofty/tests/taglib/test_ape.rs diff --git a/tests/taglib/test_apetag.rs b/lofty/tests/taglib/test_apetag.rs similarity index 100% rename from tests/taglib/test_apetag.rs rename to lofty/tests/taglib/test_apetag.rs diff --git a/tests/taglib/test_fileref.rs b/lofty/tests/taglib/test_fileref.rs similarity index 96% rename from tests/taglib/test_fileref.rs rename to lofty/tests/taglib/test_fileref.rs index 722ce5f7..5e888580 100644 --- a/tests/taglib/test_fileref.rs +++ b/lofty/tests/taglib/test_fileref.rs @@ -1,10 +1,9 @@ use crate::temp_file; -use std::fs::File; use std::io::{Read, Seek}; use lofty::config::{GlobalOptions, ParseOptions, WriteOptions}; -use lofty::error::ErrorKind; +use lofty::error::{ErrorKind, LoftyError}; use lofty::file::{AudioFile, FileType, TaggedFile, TaggedFileExt}; use lofty::resolve::FileResolver; use lofty::tag::{Accessor, Tag, TagExt, TagType}; @@ -213,6 +212,7 @@ fn test_default_file_extensions() { // Marker test, Lofty does not replicate this API } +use lofty::io::{FileLike, Length, Truncate}; use lofty::properties::FileProperties; use rusty_fork::rusty_fork_test; @@ -244,7 +244,12 @@ rusty_fork_test! { Ok(Self) } - fn save_to(&self, _: &mut File, _: WriteOptions) -> lofty::error::Result<()> { + fn save_to(&self, _: &mut F, _: WriteOptions) -> lofty::error::Result<()> + where + F: FileLike, + LoftyError: From<::Error>, + LoftyError: From<::Error> + { unimplemented!() } diff --git a/tests/taglib/test_flac.rs b/lofty/tests/taglib/test_flac.rs similarity index 100% rename from tests/taglib/test_flac.rs rename to lofty/tests/taglib/test_flac.rs diff --git a/tests/taglib/test_flacpicture.rs b/lofty/tests/taglib/test_flacpicture.rs similarity index 100% rename from tests/taglib/test_flacpicture.rs rename to lofty/tests/taglib/test_flacpicture.rs diff --git a/tests/taglib/test_id3v1.rs b/lofty/tests/taglib/test_id3v1.rs similarity index 100% rename from tests/taglib/test_id3v1.rs rename to lofty/tests/taglib/test_id3v1.rs diff --git a/tests/taglib/test_id3v2.rs b/lofty/tests/taglib/test_id3v2.rs similarity index 100% rename from tests/taglib/test_id3v2.rs rename to lofty/tests/taglib/test_id3v2.rs diff --git a/tests/taglib/test_info.rs b/lofty/tests/taglib/test_info.rs similarity index 85% rename from tests/taglib/test_info.rs rename to lofty/tests/taglib/test_info.rs index 31231d75..fbda110e 100644 --- a/tests/taglib/test_info.rs +++ b/lofty/tests/taglib/test_info.rs @@ -1,9 +1,9 @@ -use lofty::iff::wav::RIFFInfoList; +use lofty::iff::wav::RiffInfoList; use lofty::tag::Accessor; #[test] fn test_title() { - let mut tag = RIFFInfoList::default(); + let mut tag = RiffInfoList::default(); assert!(tag.title().is_none()); tag.set_title(String::from("Test title 1")); @@ -16,7 +16,7 @@ fn test_title() { #[test] fn test_numeric_fields() { - let mut tag = RIFFInfoList::default(); + let mut tag = RiffInfoList::default(); assert!(tag.track().is_none()); tag.set_track(1234); diff --git a/tests/taglib/test_mp4.rs b/lofty/tests/taglib/test_mp4.rs similarity index 100% rename from tests/taglib/test_mp4.rs rename to lofty/tests/taglib/test_mp4.rs diff --git a/tests/taglib/test_mpc.rs b/lofty/tests/taglib/test_mpc.rs similarity index 100% rename from tests/taglib/test_mpc.rs rename to lofty/tests/taglib/test_mpc.rs diff --git a/tests/taglib/test_mpeg.rs b/lofty/tests/taglib/test_mpeg.rs similarity index 100% rename from tests/taglib/test_mpeg.rs rename to lofty/tests/taglib/test_mpeg.rs diff --git a/tests/taglib/test_ogaflac.rs b/lofty/tests/taglib/test_ogaflac.rs similarity index 100% rename from tests/taglib/test_ogaflac.rs rename to lofty/tests/taglib/test_ogaflac.rs diff --git a/tests/taglib/test_ogg.rs b/lofty/tests/taglib/test_ogg.rs similarity index 100% rename from tests/taglib/test_ogg.rs rename to lofty/tests/taglib/test_ogg.rs diff --git a/tests/taglib/test_opus.rs b/lofty/tests/taglib/test_opus.rs similarity index 100% rename from tests/taglib/test_opus.rs rename to lofty/tests/taglib/test_opus.rs diff --git a/tests/taglib/test_speex.rs b/lofty/tests/taglib/test_speex.rs similarity index 100% rename from tests/taglib/test_speex.rs rename to lofty/tests/taglib/test_speex.rs diff --git a/tests/taglib/test_synchdata.rs b/lofty/tests/taglib/test_synchdata.rs similarity index 100% rename from tests/taglib/test_synchdata.rs rename to lofty/tests/taglib/test_synchdata.rs diff --git a/tests/taglib/test_wav.rs b/lofty/tests/taglib/test_wav.rs similarity index 97% rename from tests/taglib/test_wav.rs rename to lofty/tests/taglib/test_wav.rs index dec415a8..4995ac89 100644 --- a/tests/taglib/test_wav.rs +++ b/lofty/tests/taglib/test_wav.rs @@ -3,7 +3,7 @@ use crate::util::get_file; use lofty::config::{ParseOptions, WriteOptions}; use lofty::file::AudioFile; use lofty::id3::v2::Id3v2Tag; -use lofty::iff::wav::{RIFFInfoList, WavFile, WavFormat}; +use lofty::iff::wav::{RiffInfoList, WavFile, WavFormat}; use lofty::tag::{Accessor, TagType}; use std::io::{Cursor, Read, Seek, SeekFrom}; @@ -143,7 +143,7 @@ fn test_info_tag() { file.rewind().unwrap(); assert!(f.riff_info().is_none()); - let mut riff_info = RIFFInfoList::default(); + let mut riff_info = RiffInfoList::default(); riff_info.set_title(String::from("Title")); riff_info.set_artist(String::from("Artist")); f.set_riff_info(riff_info); @@ -182,7 +182,7 @@ fn test_strip_tags() { id3v2.set_title(String::from("test title")); f.set_id3v2(id3v2); - let mut riff_info = RIFFInfoList::default(); + let mut riff_info = RiffInfoList::default(); riff_info.set_title(String::from("test title")); f.set_riff_info(riff_info); @@ -204,7 +204,7 @@ fn test_strip_tags() { assert!(f.id3v2().is_some()); assert!(f.riff_info().is_none()); - let mut riff_info = RIFFInfoList::default(); + let mut riff_info = RiffInfoList::default(); riff_info.set_title(String::from("test title")); f.set_riff_info(riff_info); @@ -304,7 +304,7 @@ fn test_file_with_garbage_appended() { // id3v2.set_title(String::from("ID3v2 Title")); // f.set_id3v2(id3v2); // - // let mut riff_info = RIFFInfoList::default(); + // let mut riff_info = RiffInfoList::default(); // riff_info.set_title(String::from("INFO Title")); // f.set_riff_info(riff_info); // diff --git a/tests/taglib/test_wavpack.rs b/lofty/tests/taglib/test_wavpack.rs similarity index 100% rename from tests/taglib/test_wavpack.rs rename to lofty/tests/taglib/test_wavpack.rs diff --git a/tests/taglib/test_xiphcomment.rs b/lofty/tests/taglib/test_xiphcomment.rs similarity index 100% rename from tests/taglib/test_xiphcomment.rs rename to lofty/tests/taglib/test_xiphcomment.rs diff --git a/tests/taglib/util/mod.rs b/lofty/tests/taglib/util/mod.rs similarity index 100% rename from tests/taglib/util/mod.rs rename to lofty/tests/taglib/util/mod.rs From fbdff5a57251a8c24e92b3f0acb2efea1dd4620d Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 4 May 2024 12:46:16 -0400 Subject: [PATCH 60/75] Tests: Update TagLib tests for recent ID3v2 changes --- lofty/tests/taglib/test_id3v2.rs | 312 +++++++++++++++++-------------- 1 file changed, 167 insertions(+), 145 deletions(-) diff --git a/lofty/tests/taglib/test_id3v2.rs b/lofty/tests/taglib/test_id3v2.rs index 5b7dded9..44da4912 100644 --- a/lofty/tests/taglib/test_id3v2.rs +++ b/lofty/tests/taglib/test_id3v2.rs @@ -9,10 +9,9 @@ use lofty::file::AudioFile; use lofty::id3::v2::{ AttachedPictureFrame, ChannelInformation, ChannelType, CommentFrame, Event, EventTimingCodesFrame, EventType, ExtendedTextFrame, ExtendedUrlFrame, Frame, FrameFlags, - FrameId, FrameValue, GeneralEncapsulatedObject, Id3v2Tag, Id3v2Version, OwnershipFrame, - Popularimeter, PrivateFrame, RelativeVolumeAdjustmentFrame, SyncTextContentType, - SynchronizedText, TextInformationFrame, TimestampFormat, UniqueFileIdentifierFrame, - UrlLinkFrame, + FrameId, GeneralEncapsulatedObject, Id3v2Tag, Id3v2Version, OwnershipFrame, PopularimeterFrame, + PrivateFrame, RelativeVolumeAdjustmentFrame, SyncTextContentType, SynchronizedTextFrame, + TextInformationFrame, TimestampFormat, UniqueFileIdentifierFrame, UrlLinkFrame, }; use lofty::mpeg::MpegFile; use lofty::picture::{MimeType, Picture, PictureType}; @@ -43,10 +42,11 @@ fn test_downgrade_utf8_for_id3v23_2() {} #[test] fn test_utf16be_delimiter() { - let mut f = TextInformationFrame { - encoding: TextEncoding::UTF16BE, - value: String::from("Foo\0Bar"), - }; + let mut f = TextInformationFrame::new( + FrameId::Valid(Cow::Borrowed("TIT2")), + TextEncoding::UTF16BE, + String::from("Foo\0Bar"), + ); let data = f.as_bytes(); @@ -55,18 +55,24 @@ fn test_utf16be_delimiter() { \0B\0a\0r"; assert_eq!(data, no_bom_be_data); - f = TextInformationFrame::parse(&mut &data[..], Id3v2Version::V4) - .unwrap() - .unwrap(); + f = TextInformationFrame::parse( + &mut &data[..], + FrameId::Valid(Cow::Borrowed("TIT2")), + FrameFlags::default(), + Id3v2Version::V4, + ) + .unwrap() + .unwrap(); assert_eq!(f.value, "Foo\0Bar"); } #[test] fn test_utf16_delimiter() { - let mut f = TextInformationFrame { - encoding: TextEncoding::UTF16, - value: String::from("Foo\0Bar"), - }; + let mut f = TextInformationFrame::new( + FrameId::Valid(Cow::Borrowed("TIT2")), + TextEncoding::UTF16, + String::from("Foo\0Bar"), + ); let data = f.as_bytes(); @@ -78,33 +84,53 @@ fn test_utf16_delimiter() { B\0a\0r\0"; assert_eq!(data, single_bom_le_data); - f = TextInformationFrame::parse(&mut &data[..], Id3v2Version::V4) - .unwrap() - .unwrap(); + f = TextInformationFrame::parse( + &mut &data[..], + FrameId::Valid(Cow::Borrowed("TIT2")), + FrameFlags::default(), + Id3v2Version::V4, + ) + .unwrap() + .unwrap(); assert_eq!(f.value, "Foo\0Bar"); let multi_bom_le_data = b"\x01\xff\xfe\ F\0o\0o\0\0\0\xff\xfe\ B\0a\0r\0"; - f = TextInformationFrame::parse(&mut &multi_bom_le_data[..], Id3v2Version::V4) - .unwrap() - .unwrap(); + f = TextInformationFrame::parse( + &mut &multi_bom_le_data[..], + FrameId::Valid(Cow::Borrowed("TIT2")), + FrameFlags::default(), + Id3v2Version::V4, + ) + .unwrap() + .unwrap(); assert_eq!(f.value, "Foo\0Bar"); let multi_bom_be_data = b"\x01\xfe\xff\ \0F\0o\0o\0\0\xfe\xff\ \0B\0a\0r"; - f = TextInformationFrame::parse(&mut &multi_bom_be_data[..], Id3v2Version::V4) - .unwrap() - .unwrap(); + f = TextInformationFrame::parse( + &mut &multi_bom_be_data[..], + FrameId::Valid(Cow::Borrowed("TIT2")), + FrameFlags::default(), + Id3v2Version::V4, + ) + .unwrap() + .unwrap(); assert_eq!(f.value, "Foo\0Bar"); let single_bom_be_data = b"\x01\xfe\xff\ \0F\0o\0o\0\0\ \0B\0a\0r"; - f = TextInformationFrame::parse(&mut &single_bom_be_data[..], Id3v2Version::V4) - .unwrap() - .unwrap(); + f = TextInformationFrame::parse( + &mut &single_bom_be_data[..], + FrameId::Valid(Cow::Borrowed("TIT2")), + FrameFlags::default(), + Id3v2Version::V4, + ) + .unwrap() + .unwrap(); assert_eq!(f.value, "Foo\0Bar"); } @@ -137,6 +163,7 @@ fn test_parse_apic() { \x01\ d\x00\ \x00"[..], + FrameFlags::default(), Id3v2Version::V4, ) .unwrap(); @@ -155,6 +182,7 @@ fn test_parse_apic_utf16_bom() { \x01\x69\x6d\x61\x67\x65\ \x2f\x6a\x70\x65\x67\x00\x00\xfe\xff\x00\x63\x00\x6f\x00\x76\x00\ \x65\x00\x72\x00\x2e\x00\x6a\x00\x70\x00\x67\x00\x00\xff\xd8\xff"[..], + FrameFlags::default(), Id3v2Version::V4, ) .unwrap(); @@ -174,6 +202,7 @@ fn test_parse_apicv22() { \x01\ d\x00\ \x00"[..], + FrameFlags::default(), Id3v2Version::V2, ) .unwrap(); @@ -185,15 +214,15 @@ fn test_parse_apicv22() { #[test] fn test_render_apic() { - let f = AttachedPictureFrame { - encoding: TextEncoding::UTF8, - picture: Picture::new_unchecked( + let f = AttachedPictureFrame::new( + TextEncoding::UTF8, + Picture::new_unchecked( PictureType::CoverBack, Some(MimeType::Png), Some(String::from("Description")), b"PNG data".to_vec(), ), - }; + ); assert_eq!( f.as_bytes(Id3v2Version::V4).unwrap(), @@ -221,6 +250,7 @@ fn test_parse_geob() { f\x00\ d\x00\ \x00", + FrameFlags::default(), ) .unwrap(); assert_eq!(f.mime_type.as_deref(), Some("m")); @@ -230,13 +260,13 @@ fn test_parse_geob() { #[test] fn test_render_geob() { - let f = GeneralEncapsulatedObject { - encoding: TextEncoding::Latin1, - mime_type: Some(String::from("application/octet-stream")), - file_name: Some(String::from("test.bin")), - descriptor: Some(String::from("Description")), - data: vec![0x01; 3], - }; + let f = GeneralEncapsulatedObject::new( + TextEncoding::Latin1, + Some(String::from("application/octet-stream")), + Some(String::from("test.bin")), + Some(String::from("Description")), + vec![0x01; 3], + ); assert_eq!( f.as_bytes(), @@ -251,11 +281,12 @@ fn test_render_geob() { #[test] fn test_parse_popm() { - let f = Popularimeter::parse( + let f = PopularimeterFrame::parse( &mut &b"\ email@example.com\x00\ \x02\ \x00\x00\x00\x03"[..], + FrameFlags::default(), ) .unwrap(); assert_eq!(f.email, "email@example.com"); @@ -265,10 +296,11 @@ fn test_parse_popm() { #[test] fn test_parse_popm_without_counter() { - let f = Popularimeter::parse( + let f = PopularimeterFrame::parse( &mut &b"\ email@example.com\x00\ \x02"[..], + FrameFlags::default(), ) .unwrap(); assert_eq!(f.email, "email@example.com"); @@ -278,11 +310,7 @@ fn test_parse_popm_without_counter() { #[test] fn test_render_popm() { - let f = Popularimeter { - email: String::from("email@example.com"), - rating: 2, - counter: 3, - }; + let f = PopularimeterFrame::new(String::from("email@example.com"), 2, 3); assert_eq!( f.as_bytes(), @@ -303,20 +331,14 @@ fn test_popm_to_string() { fn test_popm_from_file() { let mut file = temp_file!("tests/taglib/data/xing.mp3"); - let f = Popularimeter { - email: String::from("email@example.com"), - rating: 200, - counter: 3, - }; + let f = PopularimeterFrame::new(String::from("email@example.com"), 200, 3); { let mut foo = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); let mut tag = Id3v2Tag::new(); - tag.insert( - Frame::new("POPM", FrameValue::Popularimeter(f), FrameFlags::default()).unwrap(), - ); + tag.insert(Frame::Popularimeter(f)); foo.set_id3v2(tag); foo.save_to(&mut file, WriteOptions::default()).unwrap(); } @@ -328,9 +350,8 @@ fn test_popm_from_file() { .unwrap() .get(&FrameId::Valid(Cow::Borrowed("POPM"))) .unwrap(); - let popularimeter = match popm_frame.content() { - FrameValue::Popularimeter(popm) => popm, - _ => unreachable!(), + let Frame::Popularimeter(popularimeter) = popm_frame else { + unreachable!() }; assert_eq!(popularimeter.email, "email@example.com"); @@ -339,6 +360,7 @@ fn test_popm_from_file() { } #[test] +#[allow(clippy::float_cmp)] fn test_parse_relative_volume_frame() { let f = RelativeVolumeAdjustmentFrame::parse( &mut &b"\ @@ -347,6 +369,7 @@ fn test_parse_relative_volume_frame() { \x00\x0F\ \x08\ \x45"[..], + FrameFlags::default(), ParsingMode::Strict, ) .unwrap() @@ -355,7 +378,7 @@ fn test_parse_relative_volume_frame() { assert_eq!(f.identification, "ident"); let front_right = f.channels.get(&ChannelType::FrontRight).unwrap(); assert_eq!( - front_right.volume_adjustment as f32 / 512.0f32, + f32::from(front_right.volume_adjustment) / 512.0f32, 15.0f32 / 512.0f32 ); assert_eq!(front_right.volume_adjustment, 15); @@ -367,22 +390,19 @@ fn test_parse_relative_volume_frame() { #[test] fn test_render_relative_volume_frame() { - let f = RelativeVolumeAdjustmentFrame { - identification: String::from("ident"), - channels: { - let mut m = HashMap::new(); - m.insert( - ChannelType::FrontRight, - ChannelInformation { - channel_type: ChannelType::FrontRight, - volume_adjustment: 15, - bits_representing_peak: 8, - peak_volume: Some(vec![0x45]), - }, - ); - m - }, - }; + let f = RelativeVolumeAdjustmentFrame::new(String::from("ident"), { + let mut m = HashMap::new(); + m.insert( + ChannelType::FrontRight, + ChannelInformation { + channel_type: ChannelType::FrontRight, + volume_adjustment: 15, + bits_representing_peak: 8, + peak_volume: Some(vec![0x45]), + }, + ); + m + }); assert_eq!( f.as_bytes(), @@ -401,6 +421,7 @@ fn test_parse_unique_file_identifier_frame() { &mut &b"\ owner\x00\ \x00\x01\x02"[..], + FrameFlags::default(), ParsingMode::Strict, ) .unwrap() @@ -416,6 +437,7 @@ fn test_parse_empty_unique_file_identifier_frame() { &mut &b"\ \x00\ "[..], + FrameFlags::default(), ParsingMode::Strict, ); @@ -425,10 +447,7 @@ fn test_parse_empty_unique_file_identifier_frame() { #[test] fn test_render_unique_file_identifier_frame() { - let f = UniqueFileIdentifierFrame { - owner: String::from("owner"), - identifier: b"\x01\x02\x03".to_vec(), - }; + let f = UniqueFileIdentifierFrame::new(String::from("owner"), b"\x01\x02\x03".to_vec()); assert_eq!( f.as_bytes(), @@ -440,17 +459,25 @@ owner\x00\ #[test] fn test_parse_url_link_frame() { - let f = UrlLinkFrame::parse(&mut &b"http://example.com"[..]) - .unwrap() - .unwrap(); + let f = UrlLinkFrame::parse( + &mut &b"http://example.com"[..], + FrameId::Valid(Cow::Borrowed("WPUB")), + FrameFlags::default(), + ) + .unwrap() + .unwrap(); assert_eq!(f.url(), "http://example.com"); } #[test] fn test_render_url_link_frame() { - let f = UrlLinkFrame::parse(&mut &b"http://example.com"[..]) - .unwrap() - .unwrap(); + let f = UrlLinkFrame::parse( + &mut &b"http://example.com"[..], + FrameId::Valid(Cow::Borrowed("WPUB")), + FrameFlags::default(), + ) + .unwrap() + .unwrap(); assert_eq!(f.as_bytes(), b"http://example.com"); } @@ -461,6 +488,7 @@ fn test_parse_user_url_link_frame() { \x00\ foo\x00\ http://example.com"[..], + FrameFlags::default(), Id3v2Version::V4, ) .unwrap() @@ -472,11 +500,11 @@ fn test_parse_user_url_link_frame() { #[test] fn test_render_user_url_link_frame() { - let f = ExtendedUrlFrame { - encoding: TextEncoding::Latin1, - description: String::from("foo"), - content: String::from("http://example.com"), - }; + let f = ExtendedUrlFrame::new( + TextEncoding::Latin1, + String::from("foo"), + String::from("http://example.com"), + ); assert_eq!( f.as_bytes(), @@ -495,6 +523,7 @@ fn test_parse_ownership_frame() { GBP1.99\x00\ 20120905\ Beatport"[..], + FrameFlags::default(), ) .unwrap() .unwrap(); @@ -506,12 +535,12 @@ fn test_parse_ownership_frame() { #[test] fn test_render_ownership_frame() { - let f = OwnershipFrame { - encoding: TextEncoding::Latin1, - price_paid: String::from("GBP1.99"), - date_of_purchase: String::from("20120905"), - seller: String::from("Beatport"), - }; + let f = OwnershipFrame::new( + TextEncoding::Latin1, + String::from("GBP1.99"), + String::from("20120905"), + String::from("Beatport"), + ); assert_eq!( f.as_bytes().unwrap(), @@ -525,7 +554,7 @@ fn test_render_ownership_frame() { #[test] fn test_parse_synchronized_lyrics_frame() { - let f = SynchronizedText::parse( + let f = SynchronizedTextFrame::parse( b"\ \x00\ eng\ @@ -536,6 +565,7 @@ Example\x00\ \x00\x00\x04\xd2\ Lyrics\x00\ \x00\x00\x11\xd7", + FrameFlags::default(), ) .unwrap(); @@ -554,7 +584,7 @@ Lyrics\x00\ #[test] fn test_parse_synchronized_lyrics_frame_with_empty_description() { - let f = SynchronizedText::parse( + let f = SynchronizedTextFrame::parse( b"\ \x00\ eng\ @@ -565,6 +595,7 @@ fn test_parse_synchronized_lyrics_frame_with_empty_description() { \x00\x00\x04\xd2\ Lyrics\x00\ \x00\x00\x11\xd7", + FrameFlags::default(), ) .unwrap(); @@ -583,17 +614,17 @@ fn test_parse_synchronized_lyrics_frame_with_empty_description() { #[test] fn test_render_synchronized_lyrics_frame() { - let f = SynchronizedText { - encoding: TextEncoding::Latin1, - language: *b"eng", - timestamp_format: TimestampFormat::MS, - content_type: SyncTextContentType::Lyrics, - description: Some(String::from("foo")), - content: vec![ + let f = SynchronizedTextFrame::new( + TextEncoding::Latin1, + *b"eng", + TimestampFormat::MS, + SyncTextContentType::Lyrics, + Some(String::from("foo")), + vec![ (1234, String::from("Example")), (4567, String::from("Lyrics")), ], - }; + ); assert_eq!( f.as_bytes().unwrap(), @@ -619,6 +650,7 @@ fn test_parse_event_timing_codes_frame() { \x00\x00\xf3\x5c\ \xfe\ \x00\x36\xee\x80"[..], + FrameFlags::default(), ) .unwrap() .unwrap(); @@ -630,24 +662,24 @@ fn test_parse_event_timing_codes_frame() { assert_eq!(sel[0].event_type, EventType::IntroStart); assert_eq!(sel[0].timestamp, 62300); assert_eq!(sel[1].event_type, EventType::AudioFileEnds); - assert_eq!(sel[1].timestamp, 3600000); + assert_eq!(sel[1].timestamp, 3_600_000); } #[test] fn test_render_event_timing_codes_frame() { - let f = EventTimingCodesFrame { - timestamp_format: TimestampFormat::MS, - events: vec![ + let f = EventTimingCodesFrame::new( + TimestampFormat::MS, + vec![ Event { event_type: EventType::IntroStart, timestamp: 62300, }, Event { event_type: EventType::AudioFileEnds, - timestamp: 3600000, + timestamp: 3_600_000, }, ], - }; + ); assert_eq!( f.as_bytes(), @@ -667,6 +699,7 @@ fn test_parse_comments_frame() { deu\ Description\x00\ Text"[..], + FrameFlags::default(), Id3v2Version::V4, ) .unwrap() @@ -680,12 +713,12 @@ fn test_parse_comments_frame() { #[test] fn test_render_comments_frame() { - let f = CommentFrame { - encoding: TextEncoding::UTF16, - language: *b"eng", - description: String::from("Description"), - content: String::from("Text"), - }; + let f = CommentFrame::new( + TextEncoding::UTF16, + *b"eng", + String::from("Description"), + String::from("Text"), + ); assert_eq!( f.as_bytes().unwrap(), @@ -715,6 +748,7 @@ fn test_parse_private_frame() { &mut &b"\ WM/Provider\x00\ TL"[..], + FrameFlags::default(), ) .unwrap() .unwrap(); @@ -725,10 +759,7 @@ fn test_parse_private_frame() { #[test] fn test_render_private_frame() { - let f = PrivateFrame { - owner: String::from("WM/Provider"), - private_data: b"TL".to_vec(), - }; + let f = PrivateFrame::new(String::from("WM/Provider"), b"TL".to_vec()); assert_eq!( f.as_bytes(), @@ -745,6 +776,7 @@ fn test_parse_user_text_identification_frame() { \x00\ \x00\ Text"[..], + FrameFlags::default(), Id3v2Version::V4, ) .unwrap() @@ -758,6 +790,7 @@ fn test_parse_user_text_identification_frame() { \x00\ Description\x00\ Text"[..], + FrameFlags::default(), Id3v2Version::V4, ) .unwrap() @@ -771,11 +804,7 @@ fn test_parse_user_text_identification_frame() { #[test] fn test_render_user_text_identification_frame() { - let mut f = ExtendedTextFrame { - encoding: TextEncoding::Latin1, - description: String::new(), - content: String::from("Text"), - }; + let mut f = ExtendedTextFrame::new(TextEncoding::Latin1, String::new(), String::from("Text")); assert_eq!( f.as_bytes(), @@ -826,19 +855,12 @@ fn test_save_utf16_comment() { file.rewind().unwrap(); let mut tag = Id3v2Tag::new(); - tag.insert( - Frame::new( - "COMM", - CommentFrame { - encoding: TextEncoding::UTF16, - language: *b"eng", - description: String::new(), - content: String::from("Test comment!"), - }, - FrameFlags::default(), - ) - .unwrap(), - ); + tag.insert(Frame::Comment(CommentFrame::new( + TextEncoding::UTF16, + *b"eng", + String::new(), + String::from("Test comment!"), + ))); foo.set_id3v2(tag); foo.save_to(&mut file, WriteOptions::default()).unwrap(); } @@ -874,13 +896,15 @@ fn test_update_genre_24() { let frame_value = TextInformationFrame::parse( &mut &b"\x00\ 14\0Eurodisco"[..], + FrameId::Valid(Cow::Borrowed("TCON")), + FrameFlags::default(), Id3v2Version::V4, ) .unwrap() .unwrap(); let mut tag = Id3v2Tag::new(); - tag.insert(Frame::new("TCON", frame_value, FrameFlags::default()).unwrap()); + tag.insert(Frame::Text(frame_value)); assert_eq!(tag.genre().as_deref(), Some("R&B / Eurodisco")); } @@ -928,9 +952,8 @@ fn test_compressed_frame_with_broken_length() { .unwrap() .get(&FrameId::Valid(Cow::Borrowed("APIC"))) .unwrap(); - let picture = match frame.content() { - FrameValue::Picture(AttachedPictureFrame { picture, .. }) => picture, - _ => unreachable!(), + let Frame::Picture(AttachedPictureFrame { picture, .. }) = frame else { + unreachable!() }; assert_eq!(picture.mime_type(), Some(&MimeType::Bmp)); @@ -953,9 +976,8 @@ fn test_w000() { .unwrap() .get(&FrameId::Valid(Cow::Borrowed("W000"))) .unwrap(); - let url_frame = match frame.content() { - FrameValue::Url(url_frame) => url_frame, - _ => unreachable!(), + let Frame::Url(url_frame) = frame else { + unreachable!() }; assert_eq!(url_frame.url(), "lukas.lalinsky@example.com____"); } From 018db0626cb362def15cd0e8a47e73490fa39998 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 4 May 2024 16:39:04 -0400 Subject: [PATCH 61/75] Tests: Run Clippy on TagLib tests --- lofty/tests/taglib/test_mpeg.rs | 10 +++++----- lofty/tests/taglib/test_ogg.rs | 6 +++--- lofty/tests/taglib/test_speex.rs | 4 ++-- lofty/tests/taglib/test_wavpack.rs | 10 +++++----- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lofty/tests/taglib/test_mpeg.rs b/lofty/tests/taglib/test_mpeg.rs index 56bf709b..917c868b 100644 --- a/lofty/tests/taglib/test_mpeg.rs +++ b/lofty/tests/taglib/test_mpeg.rs @@ -17,7 +17,7 @@ fn test_audio_properties_xing_header_cbr() { let f = get_file::("tests/taglib/data/lame_cbr.mp3"); assert_eq!(f.properties().duration().as_secs(), 1887); // TODO: Off by 9 - assert_eq!(f.properties().duration().as_millis(), 1887164); + assert_eq!(f.properties().duration().as_millis(), 1_887_164); assert_eq!(f.properties().audio_bitrate(), 64); assert_eq!(f.properties().channels(), 1); assert_eq!(f.properties().sample_rate(), 44100); @@ -30,7 +30,7 @@ fn test_audio_properties_xing_header_vbr() { let f = get_file::("tests/taglib/data/lame_vbr.mp3"); assert_eq!(f.properties().duration().as_secs(), 1887); // TODO: Off by 9 - assert_eq!(f.properties().duration().as_millis(), 1887164); + assert_eq!(f.properties().duration().as_millis(), 1_887_164); assert_eq!(f.properties().audio_bitrate(), 70); assert_eq!(f.properties().channels(), 1); assert_eq!(f.properties().sample_rate(), 44100); @@ -43,7 +43,7 @@ fn test_audio_properties_vbri_header() { let f = get_file::("tests/taglib/data/rare_frames.mp3"); assert_eq!(f.properties().duration().as_secs(), 222); // TODO: Off by 1 - assert_eq!(f.properties().duration().as_millis(), 222198); + assert_eq!(f.properties().duration().as_millis(), 222_198); assert_eq!(f.properties().audio_bitrate(), 233); assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().sample_rate(), 44100); @@ -55,7 +55,7 @@ fn test_audio_properties_vbri_header() { fn test_audio_properties_no_vbr_headers() { let f = get_file::("tests/taglib/data/bladeenc.mp3"); - assert_eq!(f.properties().duration().as_secs(), 3); // Off by 1 + assert_eq!(f.properties().duration().as_secs(), 3); assert_eq!(f.properties().duration().as_millis(), 3553); assert_eq!(f.properties().audio_bitrate(), 64); assert_eq!(f.properties().channels(), 1); @@ -102,7 +102,7 @@ fn test_skip_invalid_frames_3() { fn test_version_2_duration_with_xing_header() { let f = get_file::("tests/taglib/data/mpeg2.mp3"); assert_eq!(f.properties().duration().as_secs(), 5387); // TODO: Off by 15 - assert_eq!(f.properties().duration().as_millis(), 5387285); + assert_eq!(f.properties().duration().as_millis(), 5_387_285); } #[test] diff --git a/lofty/tests/taglib/test_ogg.rs b/lofty/tests/taglib/test_ogg.rs index 43e5376d..b80e62fe 100644 --- a/lofty/tests/taglib/test_ogg.rs +++ b/lofty/tests/taglib/test_ogg.rs @@ -85,7 +85,7 @@ fn test_audio_properties() { assert_eq!(f.properties().sample_rate(), 44100); assert_eq!(f.properties().version(), 0); assert_eq!(f.properties().bitrate_max(), 0); - assert_eq!(f.properties().bitrate_nominal(), 112000); + assert_eq!(f.properties().bitrate_nominal(), 112_000); assert_eq!(f.properties().bitrate_min(), 0); } @@ -104,7 +104,7 @@ fn test_page_checksum() { f.save_to(&mut file, WriteOptions::default()).unwrap(); file.seek(SeekFrom::Start(0x50)).unwrap(); - assert_eq!(file.read_u32::().unwrap(), 0x3D3BD92D); + assert_eq!(file.read_u32::().unwrap(), 0x3D3B_D92D); } file.rewind().unwrap(); { @@ -116,7 +116,7 @@ fn test_page_checksum() { f.save_to(&mut file, WriteOptions::default()).unwrap(); file.seek(SeekFrom::Start(0x50)).unwrap(); - assert_eq!(file.read_u32::().unwrap(), 0xD985291C); + assert_eq!(file.read_u32::().unwrap(), 0xD985_291C); } } diff --git a/lofty/tests/taglib/test_speex.rs b/lofty/tests/taglib/test_speex.rs index 764a0a2b..aeb6d978 100644 --- a/lofty/tests/taglib/test_speex.rs +++ b/lofty/tests/taglib/test_speex.rs @@ -42,14 +42,14 @@ fn test_split_packets() { let mut f = SpeexFile::read_from(&mut file, ParseOptions::new()).unwrap(); file.rewind().unwrap(); - assert_eq!(file.metadata().unwrap().len(), 156330); + assert_eq!(file.metadata().unwrap().len(), 156_330); assert_eq!(f.vorbis_comments().title().as_deref(), Some(text.as_str())); // NOTE: TagLib exposes the packets and page headers through `Speex::File`. // Lofty does not keep this information around, so we just double check with `ogg_pager`. let packets = ogg_pager::Packets::read(&mut file).unwrap(); assert_eq!(packets.get(0).unwrap().len(), 80); - assert_eq!(packets.get(1).unwrap().len(), 131116); + assert_eq!(packets.get(1).unwrap().len(), 131_116); assert_eq!(packets.get(2).unwrap().len(), 93); assert_eq!(packets.get(3).unwrap().len(), 93); diff --git a/lofty/tests/taglib/test_wavpack.rs b/lofty/tests/taglib/test_wavpack.rs index 74d673dd..692bd0b6 100644 --- a/lofty/tests/taglib/test_wavpack.rs +++ b/lofty/tests/taglib/test_wavpack.rs @@ -19,7 +19,7 @@ fn test_no_length_properties() { assert_eq!(f.properties().audio_bitrate(), 1); assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().bit_depth(), 16); - assert_eq!(f.properties().is_lossless(), true); + assert!(f.properties().is_lossless()); assert_eq!(f.properties().sample_rate(), 44100); // TODO: CPPUNIT_ASSERT_EQUAL(163392U, f.audioProperties()->sampleFrames()); assert_eq!(f.properties().version(), 1031); @@ -42,8 +42,8 @@ fn test_dsd_stereo_properties() { assert_eq!(f.properties().audio_bitrate(), 2096); assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().bit_depth(), 8); - assert_eq!(f.properties().is_lossless(), true); - assert_eq!(f.properties().sample_rate(), 352800); + assert!(f.properties().is_lossless()); + assert_eq!(f.properties().sample_rate(), 352_800); // TODO: CPPUNIT_ASSERT_EQUAL(70560U, f.audioProperties()->sampleFrames()); assert_eq!(f.properties().version(), 1040); } @@ -56,7 +56,7 @@ fn test_non_standard_rate_properties() { assert_eq!(f.properties().audio_bitrate(), 0); assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().bit_depth(), 16); - assert_eq!(f.properties().is_lossless(), true); + assert!(f.properties().is_lossless()); assert_eq!(f.properties().sample_rate(), 1000); // TODO: CPPUNIT_ASSERT_EQUAL(3675U, f.audioProperties()->sampleFrames()); assert_eq!(f.properties().version(), 1040); @@ -70,7 +70,7 @@ fn test_tagged_properties() { assert_eq!(f.properties().audio_bitrate(), 172); assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().bit_depth(), 16); - assert_eq!(f.properties().is_lossless(), false); + assert!(!f.properties().is_lossless()); assert_eq!(f.properties().sample_rate(), 44100); // TODO: CPPUNIT_ASSERT_EQUAL(156556U, f.audioProperties()->sampleFrames()); assert_eq!(f.properties().version(), 1031); From 4e328b453e1657ea068d6b783cb0768e6ac916aa Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 6 May 2024 13:11:12 -0400 Subject: [PATCH 62/75] Tests/TagLib: Do not check bit depth for lossy formats --- lofty/tests/taglib/test_mp4.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lofty/tests/taglib/test_mp4.rs b/lofty/tests/taglib/test_mp4.rs index 714ace31..670cf0f4 100644 --- a/lofty/tests/taglib/test_mp4.rs +++ b/lofty/tests/taglib/test_mp4.rs @@ -18,7 +18,8 @@ fn test_properties_aac() { assert_eq!(f.properties().audio_bitrate(), 3); assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().sample_rate(), 44100); - assert_eq!(f.properties().bit_depth(), Some(16)); + // NOTE: TagLib reports 16, but the stream is a lossy codec. We ignore it in this case. + assert!(f.properties().bit_depth().is_none()); assert!(!f.properties().is_drm_protected()); assert_eq!(f.properties().codec(), &Mp4Codec::AAC); } @@ -82,7 +83,7 @@ fn test_properties_alac_without_bitrate() { assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().sample_rate(), 44100); assert_eq!(f.properties().bit_depth(), Some(16)); - assert_eq!(f.properties().is_drm_protected(), false); + assert!(!f.properties().is_drm_protected()); assert_eq!(f.properties().codec(), &Mp4Codec::ALAC); } From 84e3fec15a521081bdcb76d53e7af8156281213b Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 9 May 2024 13:17:45 -0400 Subject: [PATCH 63/75] Tests: Update TagLib Musepack tests --- lofty/tests/taglib/test_mpc.rs | 42 +++++++++------------------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/lofty/tests/taglib/test_mpc.rs b/lofty/tests/taglib/test_mpc.rs index 32ba5889..928fbb2b 100644 --- a/lofty/tests/taglib/test_mpc.rs +++ b/lofty/tests/taglib/test_mpc.rs @@ -22,7 +22,8 @@ fn test_properties_sv8() { assert_eq!(properties.version(), 8); assert_eq!(properties.duration().as_secs(), 1); assert_eq!(properties.duration().as_millis(), 1497); - assert_eq!(properties.audio_bitrate(), 1); + // NOTE: TagLib reports 1, but since it's an empty stream, it should be 0 (FFmpeg reports 0) + assert_eq!(properties.average_bitrate(), 0); assert_eq!(properties.channels(), 2); assert_eq!(properties.sample_rate(), 44100); // TODO @@ -38,8 +39,12 @@ fn test_properties_sv7() { }; assert_eq!(properties.duration().as_secs(), 0); - assert_eq!(properties.duration().as_millis(), 40); - assert_eq!(properties.audio_bitrate(), 318); + // NOTE: TagLib reports 70, we report 78 like FFmpeg + assert_eq!(properties.duration().as_millis(), 78); + // No decoder can agree on this, TagLib and FFmpeg report wildly different values. + // We are able to produce the same value as `mpcdec` (the reference Musepack decoder), so + // we'll stick with that. + assert_eq!(properties.average_bitrate(), 206); assert_eq!(properties.channels(), 2); assert_eq!(properties.sample_rate(), 44100); // TODO @@ -53,38 +58,13 @@ fn test_properties_sv7() { #[test] fn test_properties_sv5() { - let f = get_file::("tests/taglib/data/sv5_header.mpc"); - - let MpcProperties::Sv4to6(properties) = f.properties() else { - panic!("Got the wrong properties somehow") - }; - - assert_eq!(properties.stream_version(), 5); - assert_eq!(properties.duration().as_secs(), 26); - assert_eq!(properties.duration().as_millis(), 26371); - assert_eq!(properties.audio_bitrate(), 0); - assert_eq!(properties.channels(), 2); - assert_eq!(properties.sample_rate(), 44100); - // TODO - // assert_eq!(properties.sample_frames(), 1162944); + // Marker test, TagLib doesn't seem to produce the correct properties for SV5 } #[test] +#[ignore] fn test_properties_sv4() { - let f = get_file::("tests/taglib/data/sv4_header.mpc"); - - let MpcProperties::Sv4to6(properties) = f.properties() else { - panic!("Got the wrong properties somehow") - }; - - assert_eq!(properties.stream_version(), 4); - assert_eq!(properties.duration().as_secs(), 26); - assert_eq!(properties.duration().as_millis(), 26371); - assert_eq!(properties.audio_bitrate(), 0); - assert_eq!(properties.channels(), 2); - assert_eq!(properties.sample_rate(), 44100); - // TODO - // assert_eq!(properties.sample_frames(), 1162944); + // Marker test, TagLib doesn't seem to produce the correct properties for SV4 } #[test] From c61869e28f8df3823661e67f02684e05dcbdb412 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 6 Jun 2024 11:46:17 -0400 Subject: [PATCH 64/75] Tests: Uncomment TagLib WAV test --- lofty/tests/taglib/test_wav.rs | 85 +++++++++++++++++----------------- 1 file changed, 42 insertions(+), 43 deletions(-) diff --git a/lofty/tests/taglib/test_wav.rs b/lofty/tests/taglib/test_wav.rs index 4995ac89..1326ec70 100644 --- a/lofty/tests/taglib/test_wav.rs +++ b/lofty/tests/taglib/test_wav.rs @@ -6,7 +6,7 @@ use lofty::id3::v2::Id3v2Tag; use lofty::iff::wav::{RiffInfoList, WavFile, WavFormat}; use lofty::tag::{Accessor, TagType}; -use std::io::{Cursor, Read, Seek, SeekFrom}; +use std::io::{Cursor, Read, Seek, SeekFrom, Write}; #[test] fn test_pcm_properties() { @@ -280,48 +280,47 @@ fn test_fuzzed_file2() { #[test] fn test_file_with_garbage_appended() { - todo!("Doesn't pass, need to check what TagLib is doing here") - // let mut file = temp_file!("tests/taglib/data/empty.wav"); - // let contents_before_modification; - // { - // file.seek(SeekFrom::End(0)).unwrap(); - // - // let garbage = b"12345678"; - // file.write_all(garbage).unwrap(); - // file.rewind().unwrap(); - // - // let mut file_contents = Vec::new(); - // file.read_to_end(&mut file_contents).unwrap(); - // - // contents_before_modification = file_contents; - // } - // file.rewind().unwrap(); - // { - // let mut f = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); - // file.rewind().unwrap(); - // - // let mut id3v2 = Id3v2Tag::default(); - // id3v2.set_title(String::from("ID3v2 Title")); - // f.set_id3v2(id3v2); - // - // let mut riff_info = RiffInfoList::default(); - // riff_info.set_title(String::from("INFO Title")); - // f.set_riff_info(riff_info); - // - // f.save_to(&mut file).unwrap(); - // } - // file.rewind().unwrap(); - // { - // TagType::Id3v2.remove_from(&mut file).unwrap(); - // file.rewind().unwrap(); - // TagType::RiffInfo.remove_from(&mut file).unwrap(); - // } - // file.rewind().unwrap(); - // { - // let mut contents_after_modification = Vec::new(); - // file.read_to_end(&mut contents_after_modification).unwrap(); - // assert_eq!(contents_before_modification, contents_after_modification); - // } + let mut file = temp_file!("tests/taglib/data/empty.wav"); + let contents_before_modification; + { + file.seek(SeekFrom::End(0)).unwrap(); + + let garbage = b"12345678"; + file.write_all(garbage).unwrap(); + file.rewind().unwrap(); + + let mut file_contents = Vec::new(); + file.read_to_end(&mut file_contents).unwrap(); + + contents_before_modification = file_contents; + } + file.rewind().unwrap(); + { + let mut f = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); + file.rewind().unwrap(); + + let mut id3v2 = Id3v2Tag::default(); + id3v2.set_title(String::from("ID3v2 Title")); + f.set_id3v2(id3v2); + + let mut riff_info = RiffInfoList::default(); + riff_info.set_title(String::from("INFO Title")); + f.set_riff_info(riff_info); + + f.save_to(&mut file, WriteOptions::default()).unwrap(); + } + file.rewind().unwrap(); + { + TagType::Id3v2.remove_from(&mut file).unwrap(); + file.rewind().unwrap(); + TagType::RiffInfo.remove_from(&mut file).unwrap(); + } + file.rewind().unwrap(); + { + let mut contents_after_modification = Vec::new(); + file.read_to_end(&mut contents_after_modification).unwrap(); + assert_eq!(contents_before_modification, contents_after_modification); + } } #[test] From 7391088dc1c549a2f3a3946e3a9d2083d8491b0f Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 4 Jul 2024 12:08:01 -0400 Subject: [PATCH 65/75] Tests: Update TagLib ID3v2 tests for recent changes --- lofty/tests/taglib/test_id3v2.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lofty/tests/taglib/test_id3v2.rs b/lofty/tests/taglib/test_id3v2.rs index 44da4912..333b31de 100644 --- a/lofty/tests/taglib/test_id3v2.rs +++ b/lofty/tests/taglib/test_id3v2.rs @@ -48,7 +48,7 @@ fn test_utf16be_delimiter() { String::from("Foo\0Bar"), ); - let data = f.as_bytes(); + let data = f.as_bytes(false); let no_bom_be_data = b"\x02\ \0F\0o\0o\0\0\ @@ -74,7 +74,7 @@ fn test_utf16_delimiter() { String::from("Foo\0Bar"), ); - let data = f.as_bytes(); + let data = f.as_bytes(false); // TODO: TagLib writes a BOM to every string, making the output identical to `mutli_bom_le_data`, // rather than `single_bom_le_data` in Lofty's case. Not sure if we should be writing the BOM @@ -507,7 +507,7 @@ fn test_render_user_url_link_frame() { ); assert_eq!( - f.as_bytes(), + f.as_bytes(false), b"\ \x00\ foo\x00\ @@ -543,7 +543,7 @@ fn test_render_ownership_frame() { ); assert_eq!( - f.as_bytes().unwrap(), + f.as_bytes(false).unwrap(), b"\ \x00\ GBP1.99\x00\ @@ -721,7 +721,7 @@ fn test_render_comments_frame() { ); assert_eq!( - f.as_bytes().unwrap(), + f.as_bytes(false).unwrap(), b"\ \x01\ eng\ @@ -807,7 +807,7 @@ fn test_render_user_text_identification_frame() { let mut f = ExtendedTextFrame::new(TextEncoding::Latin1, String::new(), String::from("Text")); assert_eq!( - f.as_bytes(), + f.as_bytes(false), b"\ \x00\ \x00\ @@ -817,7 +817,7 @@ fn test_render_user_text_identification_frame() { f.description = String::from("Description"); assert_eq!( - f.as_bytes(), + f.as_bytes(false), b"\ \x00\ Description\x00\ From 40ff8f466660d3f7a188e3632255180d94e94fc8 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 15 Jul 2024 13:12:44 -0400 Subject: [PATCH 66/75] Tests: Impl TagLib `TestWAV::testSaveID3v23` --- lofty/tests/taglib/test_wav.rs | 48 ++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/lofty/tests/taglib/test_wav.rs b/lofty/tests/taglib/test_wav.rs index 1326ec70..b3a5342f 100644 --- a/lofty/tests/taglib/test_wav.rs +++ b/lofty/tests/taglib/test_wav.rs @@ -2,7 +2,7 @@ use crate::temp_file; use crate::util::get_file; use lofty::config::{ParseOptions, WriteOptions}; use lofty::file::AudioFile; -use lofty::id3::v2::Id3v2Tag; +use lofty::id3::v2::{Id3v2Tag, Id3v2Version}; use lofty::iff::wav::{RiffInfoList, WavFile, WavFormat}; use lofty::tag::{Accessor, TagType}; @@ -108,30 +108,32 @@ fn test_id3v2_tag() { } } -// TODO: Support downgrading to ID3v2.3 (#62) #[test] -#[ignore] fn test_save_id3v23() { - // let mut file = temp_file!("tests/taglib/data/empty.wav"); - // - // let xxx = "X".repeat(254); - // { - // let mut f = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); - // assert!(f.id3v2().is_none()); - // - // f.id3v2_mut().unwrap().set_title(xxx.clone()); - // f.id3v2_mut().unwrap().set_artist(String::from("Artist A")); - // - // // f.save(RIFF::WAV::File::AllTags, File::StripOthers, ID3v2::v3); - // // assert!(f.id3v2().is_some()); - // } - // { - // let f2 = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); - // let tag = f2.id3v2().unwrap(); - // assert_eq!(tag.original_version(), Id3v2Version::V3); - // assert_eq!(tag.artist().as_deref(), Some("Artist A")); - // assert_eq!(tag.title().as_deref(), Some(&*xxx)); - // } + let mut file = temp_file!("tests/taglib/data/empty.wav"); + + let xxx = "X".repeat(254); + { + let mut f = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(f.id3v2().is_none()); + + let mut id3v2 = Id3v2Tag::new(); + id3v2.set_title(xxx.clone()); + id3v2.set_artist(String::from("Artist A")); + f.set_id3v2(id3v2); + + f.save_to(&mut file, WriteOptions::new().use_id3v23(true)) + .unwrap(); + assert!(f.id3v2().is_some()); + } + file.rewind().unwrap(); + { + let f2 = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let tag = f2.id3v2().unwrap(); + assert_eq!(tag.original_version(), Id3v2Version::V3); + assert_eq!(tag.artist().as_deref(), Some("Artist A")); + assert_eq!(tag.title().as_deref(), Some(&*xxx)); + } } #[test] From 5cb6098724e197bc4612e78333f3f5fd8ae6fde3 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Tue, 16 Jul 2024 15:09:46 -0400 Subject: [PATCH 67/75] Tests: Add ID3v2.3 tests from TagLib --- lofty/tests/taglib/test_id3v2.rs | 338 +++++++++++++++++++++++++++++-- 1 file changed, 319 insertions(+), 19 deletions(-) diff --git a/lofty/tests/taglib/test_id3v2.rs b/lofty/tests/taglib/test_id3v2.rs index 333b31de..49ce45c3 100644 --- a/lofty/tests/taglib/test_id3v2.rs +++ b/lofty/tests/taglib/test_id3v2.rs @@ -2,19 +2,21 @@ use crate::temp_file; use std::borrow::Cow; use std::collections::HashMap; -use std::io::Seek; +use std::io::{Read, Seek}; use lofty::config::{ParseOptions, ParsingMode, WriteOptions}; use lofty::file::AudioFile; use lofty::id3::v2::{ AttachedPictureFrame, ChannelInformation, ChannelType, CommentFrame, Event, EventTimingCodesFrame, EventType, ExtendedTextFrame, ExtendedUrlFrame, Frame, FrameFlags, - FrameId, GeneralEncapsulatedObject, Id3v2Tag, Id3v2Version, OwnershipFrame, PopularimeterFrame, - PrivateFrame, RelativeVolumeAdjustmentFrame, SyncTextContentType, SynchronizedTextFrame, - TextInformationFrame, TimestampFormat, UniqueFileIdentifierFrame, UrlLinkFrame, + FrameId, GeneralEncapsulatedObject, Id3v2Tag, Id3v2Version, KeyValueFrame, OwnershipFrame, + PopularimeterFrame, PrivateFrame, RelativeVolumeAdjustmentFrame, SyncTextContentType, + SynchronizedTextFrame, TextInformationFrame, TimestampFormat, TimestampFrame, + UniqueFileIdentifierFrame, UnsynchronizedTextFrame, UrlLinkFrame, }; use lofty::mpeg::MpegFile; use lofty::picture::{MimeType, Picture, PictureType}; +use lofty::tag::items::Timestamp; use lofty::tag::{Accessor, TagExt}; use lofty::TextEncoding; @@ -30,15 +32,66 @@ fn test_unsynch_decode() { ); } -// TODO: Support downgrading to ID3v2.3 (#62) #[test] -#[ignore] -fn test_downgrade_utf8_for_id3v23_1() {} +fn test_downgrade_utf8_for_id3v23_1() { + let mut file = temp_file!("tests/taglib/data/xing.mp3"); + + let f = TextInformationFrame::new( + FrameId::Valid(Cow::Borrowed("TPE1")), + TextEncoding::UTF8, + String::from("Foo"), + ); + + let mut id3v2 = Id3v2Tag::new(); + id3v2.insert(Frame::Text(f.clone())); + id3v2 + .save_to(&mut file, WriteOptions::new().use_id3v23(true)) + .unwrap(); + + let data = f.as_bytes(true); + assert_eq!(data.len(), 1 + 6 + 2); // NOTE: This does not include frame headers like TagLib does + + let f2 = TextInformationFrame::parse( + &mut &data[..], + FrameId::Valid(Cow::Borrowed("TPE1")), + FrameFlags::default(), + Id3v2Version::V3, + ) + .unwrap() + .unwrap(); + + assert_eq!(f.value, f2.value); + assert_eq!(f2.encoding, TextEncoding::UTF16); +} -// TODO: Support downgrading to ID3v2.3 (#62) #[test] -#[ignore] -fn test_downgrade_utf8_for_id3v23_2() {} +fn test_downgrade_utf8_for_id3v23_2() { + let mut file = temp_file!("tests/taglib/data/xing.mp3"); + + let f = UnsynchronizedTextFrame::new( + TextEncoding::UTF8, + *b"XXX", + String::new(), + String::from("Foo"), + ); + + let mut id3v2 = Id3v2Tag::new(); + id3v2.insert(Frame::UnsynchronizedText(f.clone())); + id3v2 + .save_to(&mut file, WriteOptions::new().use_id3v23(true)) + .unwrap(); + + let data = f.as_bytes(true).unwrap(); + assert_eq!(data.len(), 1 + 3 + 2 + 2 + 6 + 2); // NOTE: This does not include frame headers like TagLib does + + let f2 = + UnsynchronizedTextFrame::parse(&mut &data[..], FrameFlags::default(), Id3v2Version::V3) + .unwrap() + .unwrap(); + + assert_eq!(f2.content, String::from("Foo")); + assert_eq!(f2.encoding, TextEncoding::UTF16); +} #[test] fn test_utf16be_delimiter() { @@ -874,21 +927,82 @@ fn test_save_utf16_comment() { } } -// TODO: Support downgrading to ID3v2.3 (#62) +// TODO: Probably won't ever support this, it's a weird edge case with +// duplicate genres. That can be up to the caller to figure out. #[test] #[ignore] -fn test_update_genre_23_1() {} +fn test_update_genre_23_1() { + // "Refinement" is the same as the ID3v1 genre - duplicate + let frame_value = TextInformationFrame::parse( + &mut &b"\x00\ + (22)Death Metal"[..], + FrameId::Valid(Cow::Borrowed("TCON")), + FrameFlags::default(), + Id3v2Version::V4, + ) + .unwrap() + .unwrap(); + + let mut tag = Id3v2Tag::new(); + tag.insert(Frame::Text(frame_value)); + + let mut genres = tag.genres().unwrap(); + assert_eq!(genres.next(), Some("Death Metal")); + assert!(genres.next().is_none()); + + assert_eq!(tag.genre().as_deref(), Some("Death Metal")); +} #[test] -#[ignore] fn test_update_genre23_2() { - // Marker test, Lofty doesn't do additional work with the genre string + // "Refinement" is different from the ID3v1 genre + let frame_value = TextInformationFrame::parse( + &mut &b"\x00\ + (4)Eurodisco"[..], + FrameId::Valid(Cow::Borrowed("TCON")), + FrameFlags::default(), + Id3v2Version::V4, + ) + .unwrap() + .unwrap(); + + let mut tag = Id3v2Tag::new(); + tag.insert(Frame::Text(frame_value)); + + let mut genres = tag.genres().unwrap(); + assert_eq!(genres.next(), Some("Disco")); + assert_eq!(genres.next(), Some("Eurodisco")); + assert!(genres.next().is_none()); + + assert_eq!(tag.genre().as_deref(), Some("Disco / Eurodisco")); } #[test] -#[ignore] fn test_update_genre23_3() { - // Marker test, Lofty doesn't do additional work with the genre string + // Multiple references and a refinement + let frame_value = TextInformationFrame::parse( + &mut &b"\x00\ + (9)(138)Viking Metal"[..], + FrameId::Valid(Cow::Borrowed("TCON")), + FrameFlags::default(), + Id3v2Version::V4, + ) + .unwrap() + .unwrap(); + + let mut tag = Id3v2Tag::new(); + tag.insert(Frame::Text(frame_value)); + + let mut genres = tag.genres().unwrap(); + assert_eq!(genres.next(), Some("Metal")); + assert_eq!(genres.next(), Some("Black Metal")); + assert_eq!(genres.next(), Some("Viking Metal")); + assert!(genres.next().is_none()); + + assert_eq!( + tag.genre().as_deref(), + Some("Metal / Black Metal / Viking Metal") + ); } #[test] @@ -933,10 +1047,196 @@ fn test_update_full_date22() { ); } -// TODO: Support downgrading to ID3v2.3 (#62) #[test] -#[ignore] -fn test_downgrade_to_23() {} +fn test_downgrade_to_23() { + let mut file = temp_file!("tests/taglib/data/xing.mp3"); + + { + let mut id3v2 = Id3v2Tag::new(); + + id3v2.insert(Frame::Timestamp(TimestampFrame::new( + FrameId::Valid(Cow::Borrowed("TDOR")), + TextEncoding::Latin1, + Timestamp::parse(&mut &b"2011-03-16"[..], ParsingMode::Strict) + .unwrap() + .unwrap(), + ))); + + id3v2.insert(Frame::Timestamp(TimestampFrame::new( + FrameId::Valid(Cow::Borrowed("TDRC")), + TextEncoding::Latin1, + Timestamp::parse(&mut &b"2012-04-17T12:01"[..], ParsingMode::Strict) + .unwrap() + .unwrap(), + ))); + + id3v2.insert(Frame::KeyValue(KeyValueFrame::new( + FrameId::Valid(Cow::Borrowed("TMCL")), + TextEncoding::Latin1, + vec![ + (String::from("Guitar"), String::from("Artist 1")), + (String::from("Drums"), String::from("Artist 2")), + ], + ))); + + id3v2.insert(Frame::KeyValue(KeyValueFrame::new( + FrameId::Valid(Cow::Borrowed("TIPL")), + TextEncoding::Latin1, + vec![ + (String::from("Producer"), String::from("Artist 3")), + (String::from("Mastering"), String::from("Artist 4")), + ], + ))); + + id3v2.insert(Frame::Text(TextInformationFrame::new( + FrameId::Valid(Cow::Borrowed("TCON")), + TextEncoding::Latin1, + String::from("51\039\0Power Noise"), + ))); + + id3v2.insert(Frame::Text(TextInformationFrame::new( + FrameId::Valid(Cow::Borrowed("TDRL")), + TextEncoding::Latin1, + String::new(), + ))); + + id3v2.insert(Frame::Text(TextInformationFrame::new( + FrameId::Valid(Cow::Borrowed("TDTG")), + TextEncoding::Latin1, + String::new(), + ))); + + id3v2.insert(Frame::Text(TextInformationFrame::new( + FrameId::Valid(Cow::Borrowed("TMOO")), + TextEncoding::Latin1, + String::new(), + ))); + + id3v2.insert(Frame::Text(TextInformationFrame::new( + FrameId::Valid(Cow::Borrowed("TPRO")), + TextEncoding::Latin1, + String::new(), + ))); + + id3v2.insert(Frame::Text(TextInformationFrame::new( + FrameId::Valid(Cow::Borrowed("TSOA")), + TextEncoding::Latin1, + String::new(), + ))); + + id3v2.insert(Frame::Text(TextInformationFrame::new( + FrameId::Valid(Cow::Borrowed("TSOT")), + TextEncoding::Latin1, + String::new(), + ))); + + id3v2.insert(Frame::Text(TextInformationFrame::new( + FrameId::Valid(Cow::Borrowed("TSST")), + TextEncoding::Latin1, + String::new(), + ))); + + id3v2.insert(Frame::Text(TextInformationFrame::new( + FrameId::Valid(Cow::Borrowed("TSOP")), + TextEncoding::Latin1, + String::new(), + ))); + + id3v2 + .save_to(&mut file, WriteOptions::new().use_id3v23(true)) + .unwrap(); + } + file.rewind().unwrap(); + { + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(f.id3v2().is_some()); + + let id3v2 = f.id3v2().unwrap(); + let tf = id3v2.get(&FrameId::Valid(Cow::Borrowed("TDOR"))).unwrap(); + let Frame::Timestamp(TimestampFrame { timestamp, .. }) = tf else { + unreachable!() + }; + assert_eq!(timestamp.to_string(), "2011"); + + let tf = id3v2.get(&FrameId::Valid(Cow::Borrowed("TDRC"))).unwrap(); + let Frame::Timestamp(TimestampFrame { timestamp, .. }) = tf else { + unreachable!() + }; + assert_eq!(timestamp.to_string(), "2012-04-17T12:01"); + + let tf = id3v2.get(&FrameId::Valid(Cow::Borrowed("TIPL"))).unwrap(); + let Frame::KeyValue(KeyValueFrame { + key_value_pairs, .. + }) = tf + else { + unreachable!() + }; + assert_eq!(key_value_pairs.len(), 4); + assert_eq!( + key_value_pairs[0], + (String::from("Guitar"), String::from("Artist 1")) + ); + assert_eq!( + key_value_pairs[1], + (String::from("Drums"), String::from("Artist 2")) + ); + assert_eq!( + key_value_pairs[2], + (String::from("Producer"), String::from("Artist 3")) + ); + assert_eq!( + key_value_pairs[3], + (String::from("Mastering"), String::from("Artist 4")) + ); + + // NOTE: Lofty upgrades the first genre (originally 51) to "Techno-Industrial" + // TagLib retains the original genre index. + let tf = id3v2.genres().unwrap().collect::>(); + assert_eq!(tf.join("\0"), "Techno-Industrial\0Noise\0Power Noise"); + + assert!(!id3v2.contains(&FrameId::Valid(Cow::Borrowed("TDRL")))); + assert!(!id3v2.contains(&FrameId::Valid(Cow::Borrowed("TDTG")))); + assert!(!id3v2.contains(&FrameId::Valid(Cow::Borrowed("TMOO")))); + assert!(!id3v2.contains(&FrameId::Valid(Cow::Borrowed("TPRO")))); + assert!(!id3v2.contains(&FrameId::Valid(Cow::Borrowed("TSOA")))); + assert!(!id3v2.contains(&FrameId::Valid(Cow::Borrowed("TSOT")))); + assert!(!id3v2.contains(&FrameId::Valid(Cow::Borrowed("TSST")))); + assert!(!id3v2.contains(&FrameId::Valid(Cow::Borrowed("TSOP")))); + } + file.rewind().unwrap(); + { + #[allow(clippy::items_after_statements)] + const EXPECTED_ID3V23_DATA: &[u8] = b"ID3\x03\x00\x00\x00\x00\x09\x28\ + TORY\x00\x00\x00\x05\x00\x00\x002011\ + TYER\x00\x00\x00\x05\x00\x00\x002012\ + TDAT\x00\x00\x00\x05\x00\x00\x001704\ + TIME\x00\x00\x00\x05\x00\x00\x001201\ + TCON\x00\x00\x00\x14\x00\x00\x00(51)(39)Power Noise\ + IPLS\x00\x00\x00\x44\x00\x00\x00Guitar\x00\ + Artist 1\x00Drums\x00Artist 2\x00Producer\x00\ + Artist 3\x00Mastering\x00Artist 4"; + + let mut file_id3v2 = vec![0; EXPECTED_ID3V23_DATA.len()]; + file.read_exact(&mut file_id3v2).unwrap(); + assert_eq!(file_id3v2.as_slice(), EXPECTED_ID3V23_DATA); + } + { + let mut file = temp_file!("tests/taglib/data/rare_frames.mp3"); + let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + assert!(f.id3v2().is_some()); + file.rewind().unwrap(); + f.save_to(&mut file, WriteOptions::new().use_id3v23(true)) + .unwrap(); + + file.rewind().unwrap(); + let mut file_content = Vec::new(); + file.read_to_end(&mut file_content).unwrap(); + + let tcon_pos = file_content.windows(4).position(|w| w == b"TCON").unwrap(); + let tcon = &file_content[tcon_pos + 11..]; + assert_eq!(&tcon[..4], &b"(13)"[..]); + } +} #[test] fn test_compressed_frame_with_broken_length() { From b074dfcc1d620fa70aa0ff336291c0aefcd051ed Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Mon, 22 Jul 2024 12:12:52 -0400 Subject: [PATCH 68/75] Tests: Update TagLib MP4 test --- lofty/tests/taglib/test_mp4.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lofty/tests/taglib/test_mp4.rs b/lofty/tests/taglib/test_mp4.rs index 670cf0f4..00f43fb3 100644 --- a/lofty/tests/taglib/test_mp4.rs +++ b/lofty/tests/taglib/test_mp4.rs @@ -216,7 +216,8 @@ fn test_save_existing_when_ilst_is_last() { let mut file = temp_file!("tests/taglib/data/ilst-is-last.m4a"); { - let mut f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + let mut f = + Mp4File::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); file.rewind().unwrap(); let ilst = f.ilst_mut().unwrap(); @@ -237,7 +238,7 @@ fn test_save_existing_when_ilst_is_last() { } file.rewind().unwrap(); { - let f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = Mp4File::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); let ilst = f.ilst().unwrap(); assert_eq!( From 7f636401dffa21decb1a758d8c40b3f9620c87be Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 14 Sep 2024 17:12:09 -0400 Subject: [PATCH 69/75] Tests: Update TagLib tests for latest changes --- lofty/tests/taglib/test_id3v2.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lofty/tests/taglib/test_id3v2.rs b/lofty/tests/taglib/test_id3v2.rs index 49ce45c3..4c4fce44 100644 --- a/lofty/tests/taglib/test_id3v2.rs +++ b/lofty/tests/taglib/test_id3v2.rs @@ -366,7 +366,7 @@ fn test_render_popm() { let f = PopularimeterFrame::new(String::from("email@example.com"), 2, 3); assert_eq!( - f.as_bytes(), + f.as_bytes().unwrap(), b"\ email@example.com\x00\ \x02\ @@ -815,7 +815,7 @@ fn test_render_private_frame() { let f = PrivateFrame::new(String::from("WM/Provider"), b"TL".to_vec()); assert_eq!( - f.as_bytes(), + f.as_bytes().unwrap(), b"\ WM/Provider\x00\ TL" From 29d310f6ee87d0c03736ad83ea1b77dce0da6ed0 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Wed, 20 Nov 2024 00:22:10 -0500 Subject: [PATCH 70/75] Tests: Allow missing docs --- lofty/tests/taglib/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lofty/tests/taglib/main.rs b/lofty/tests/taglib/main.rs index 145e66ac..f695aeb6 100644 --- a/lofty/tests/taglib/main.rs +++ b/lofty/tests/taglib/main.rs @@ -1,3 +1,5 @@ +#![allow(missing_docs)] + pub(crate) mod util; mod test_aiff; From 18c4d2ed53366d2d1c4581746f557cf07ea40958 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 24 Apr 2025 00:46:07 -0400 Subject: [PATCH 71/75] Tests: Ignore TagLib `TestWAV::test_duplicate_tags` --- lofty/tests/taglib/test_wav.rs | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/lofty/tests/taglib/test_wav.rs b/lofty/tests/taglib/test_wav.rs index b3a5342f..3d782872 100644 --- a/lofty/tests/taglib/test_wav.rs +++ b/lofty/tests/taglib/test_wav.rs @@ -230,33 +230,11 @@ fn test_strip_tags() { } #[test] +#[ignore] fn test_duplicate_tags() { - let mut file = temp_file!("tests/taglib/data/duplicate_tags.wav"); - - let f = WavFile::read_from(&mut file, ParseOptions::new()).unwrap(); - assert_eq!(file.seek(SeekFrom::End(0)).unwrap(), 17052); - file.rewind().unwrap(); - - // duplicate_tags.wav has duplicate ID3v2/INFO tags. - // title() returns "Title2" if can't skip the second tag. - - assert!(f.id3v2().is_some()); - assert_eq!(f.id3v2().unwrap().title().as_deref(), Some("Title1")); - - assert!(f.riff_info().is_some()); - assert_eq!(f.riff_info().unwrap().title().as_deref(), Some("Title1")); - - f.save_to(&mut file, WriteOptions::default()).unwrap(); - assert_eq!(file.seek(SeekFrom::End(0)).unwrap(), 15898); - - let mut file_bytes = Vec::new(); - file.rewind().unwrap(); - file.read_to_end(&mut file_bytes).unwrap(); - - assert_eq!( - file_bytes.windows(6).position(|window| window == b"Title2"), - None - ); + // Marker test, TagLib will ignore any tag except for the first. Lofty will *not* do this. + // Every tag in the stream is read and merged into the previous one. Whichever tag ends up being + // the latest in the stream will have precedence. } #[test] From 27934d19c7568964cd09ec3dfd6a2817aa2b3cf4 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 24 Apr 2025 00:47:15 -0400 Subject: [PATCH 72/75] Tests: Fix TagLib `TestMPEG::test_ignore_garbage` --- lofty/tests/taglib/test_mpeg.rs | 4 ++-- lofty/tests/taglib/test_xiphcomment.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lofty/tests/taglib/test_mpeg.rs b/lofty/tests/taglib/test_mpeg.rs index 917c868b..24e2819b 100644 --- a/lofty/tests/taglib/test_mpeg.rs +++ b/lofty/tests/taglib/test_mpeg.rs @@ -293,7 +293,7 @@ fn test_ignore_garbage() { let mut file = temp_file!("tests/taglib/data/garbage.mp3"); { - let mut f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let mut f = MpegFile::read_from(&mut file, ParseOptions::new().max_junk_bytes(3000)).unwrap(); file.rewind().unwrap(); assert!(f.id3v2().is_some()); @@ -303,7 +303,7 @@ fn test_ignore_garbage() { } file.rewind().unwrap(); { - let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); + let f = MpegFile::read_from(&mut file, ParseOptions::new().max_junk_bytes(3000)).unwrap(); assert!(f.id3v2().is_some()); assert_eq!(f.id3v2().unwrap().title().as_deref(), Some("Title B")); } diff --git a/lofty/tests/taglib/test_xiphcomment.rs b/lofty/tests/taglib/test_xiphcomment.rs index 9acb2ca9..ccbc7ab8 100644 --- a/lofty/tests/taglib/test_xiphcomment.rs +++ b/lofty/tests/taglib/test_xiphcomment.rs @@ -94,7 +94,7 @@ fn test_clear_comment() { #[test] #[ignore] fn test_remove_fields() { - // Market test, TagLib has some incredibly strange behavior in this test. + // Marker test, TagLib has some incredibly strange behavior in this test. // // When adding a field of the same key, TagLib will append each value to the same value. // Meaning: From ddaa5a4088781d884ec5e83a0ceb30642bf0cb3f Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 24 Apr 2025 01:20:09 -0400 Subject: [PATCH 73/75] Tests: Use `test-log` in TagLib tests --- lofty/tests/taglib/test_aiff.rs | 14 +-- lofty/tests/taglib/test_ape.rs | 20 +-- lofty/tests/taglib/test_apetag.rs | 50 ++++---- lofty/tests/taglib/test_fileref.rs | 48 ++++---- lofty/tests/taglib/test_flac.rs | 48 ++++---- lofty/tests/taglib/test_flacpicture.rs | 4 +- lofty/tests/taglib/test_id3v1.rs | 6 +- lofty/tests/taglib/test_id3v2.rs | 163 +++++++++++++------------ lofty/tests/taglib/test_info.rs | 4 +- lofty/tests/taglib/test_mp4.rs | 50 ++++---- lofty/tests/taglib/test_mpc.rs | 20 +-- lofty/tests/taglib/test_mpeg.rs | 51 ++++---- lofty/tests/taglib/test_ogaflac.rs | 6 +- lofty/tests/taglib/test_ogg.rs | 16 +-- lofty/tests/taglib/test_opus.rs | 8 +- lofty/tests/taglib/test_speex.rs | 4 +- lofty/tests/taglib/test_synchdata.rs | 18 +-- lofty/tests/taglib/test_wav.rs | 32 ++--- lofty/tests/taglib/test_wavpack.rs | 16 +-- lofty/tests/taglib/test_xiphcomment.rs | 20 +-- 20 files changed, 304 insertions(+), 294 deletions(-) diff --git a/lofty/tests/taglib/test_aiff.rs b/lofty/tests/taglib/test_aiff.rs index 1a5df78c..a4b5a963 100644 --- a/lofty/tests/taglib/test_aiff.rs +++ b/lofty/tests/taglib/test_aiff.rs @@ -10,7 +10,7 @@ use lofty::iff::aiff::{AiffCompressionType, AiffFile}; use lofty::probe::Probe; use lofty::tag::{Accessor, TagType}; -#[test] +#[test_log::test] fn test_aiff_properties() { let file = get_file::("tests/taglib/data/empty.aiff"); @@ -26,7 +26,7 @@ fn test_aiff_properties() { assert!(properties.compression_type().is_none()); } -#[test] +#[test_log::test] fn test_aifc_properties() { let file = get_file::("tests/taglib/data/alaw.aifc"); @@ -55,7 +55,7 @@ fn test_aifc_properties() { ); } -#[test] +#[test_log::test] fn test_save_id3v2() { let mut file = temp_file!("tests/taglib/data/empty.aiff"); @@ -95,19 +95,19 @@ fn test_save_id3v2() { } } -#[test] +#[test_log::test] #[ignore] fn test_save_id3v23() { todo!("Support writing ID3v2.3 tags") } -#[test] +#[test_log::test] #[ignore] fn test_duplicate_id3v2() { // Marker test, Lofty will overwrite values in the original tag with any new values it finds in the next tag. } -#[test] +#[test_log::test] fn test_fuzzed_file1() { assert_eq!( Probe::open("tests/taglib/data/segfault.aif") @@ -119,7 +119,7 @@ fn test_fuzzed_file1() { ); } -#[test] +#[test_log::test] #[ignore] fn test_fuzzed_file2() { // Marker test, this file doesn't even have a valid signature. No idea how TagLib manages to read it. diff --git a/lofty/tests/taglib/test_ape.rs b/lofty/tests/taglib/test_ape.rs index 8c167831..d100d71a 100644 --- a/lofty/tests/taglib/test_ape.rs +++ b/lofty/tests/taglib/test_ape.rs @@ -25,22 +25,22 @@ fn test_399(path: &str) { assert_eq!(properties.version(), 3990) } -#[test] +#[test_log::test] fn test_properties_399() { test_399("tests/taglib/data/mac-399.ape") } -#[test] +#[test_log::test] fn test_properties_399_tagged() { test_399("tests/taglib/data/mac-399-tagged.ape") } -#[test] +#[test_log::test] fn test_properties_399_id3v2() { test_399("tests/taglib/data/mac-399-id3v2.ape") } -#[test] +#[test_log::test] fn test_properties_396() { let f = get_file::("tests/taglib/data/mac-396.ape"); let properties = f.properties(); @@ -55,7 +55,7 @@ fn test_properties_396() { assert_eq!(properties.version(), 3960) } -#[test] +#[test_log::test] fn test_properties_390() { let f = get_file::("tests/taglib/data/mac-390-hdr.ape"); let properties = f.properties(); @@ -70,7 +70,7 @@ fn test_properties_390() { assert_eq!(properties.version(), 3900) } -#[test] +#[test_log::test] fn test_fuzzed_file_1() { assert_eq!( Probe::open("tests/taglib/data/longloop.ape") @@ -82,7 +82,7 @@ fn test_fuzzed_file_1() { ); } -#[test] +#[test_log::test] fn test_fuzzed_file_2() { assert_eq!( Probe::open("tests/taglib/data/zerodiv.ape") @@ -94,7 +94,7 @@ fn test_fuzzed_file_2() { ); } -#[test] +#[test_log::test] fn test_strip_and_properties() { let mut file = temp_file!("tests/taglib/data/mac-399.ape"); { @@ -127,7 +127,7 @@ fn test_strip_and_properties() { } } -#[test] +#[test_log::test] fn test_properties() { let mut tag = ApeTag::default(); tag.insert( @@ -356,7 +356,7 @@ fn test_properties() { } } -#[test] +#[test_log::test] fn test_repeated_save() { let mut file = temp_file!("tests/taglib/data/mac-399.ape"); { diff --git a/lofty/tests/taglib/test_apetag.rs b/lofty/tests/taglib/test_apetag.rs index 981c100d..fc984458 100644 --- a/lofty/tests/taglib/test_apetag.rs +++ b/lofty/tests/taglib/test_apetag.rs @@ -8,7 +8,7 @@ use lofty::file::AudioFile; use lofty::musepack::MpcFile; use lofty::tag::{Accessor, ItemValue, TagExt}; -#[test] +#[test_log::test] fn test_is_empty() { let mut tag = ApeTag::default(); assert!(tag.is_empty()); @@ -22,7 +22,7 @@ fn test_is_empty() { assert!(!tag.is_empty()); } -#[test] +#[test_log::test] fn test_is_empty_2() { let mut tag = ApeTag::default(); assert!(tag.is_empty()); @@ -30,19 +30,19 @@ fn test_is_empty_2() { assert!(!tag.is_empty()); } -#[test] +#[test_log::test] #[ignore] fn test_property_interface_1() { // Marker test, Lofty does not replicate the TagLib property API } -#[test] +#[test_log::test] #[ignore] fn test_property_interface_2() { // Marker test, Lofty does not replicate the TagLib property API } -#[test] +#[test_log::test] fn test_invalid_keys() { static INVALID_KEY_ONE_CHARACTER: &str = "A"; static INVALID_KEY_FORBIDDEN_STRING: &str = "MP+"; @@ -50,21 +50,27 @@ fn test_invalid_keys() { static VALID_KEY_SPACE_AND_TILDE: &str = "A B~C"; static VALID_KEY_NORMAL_ONE: &str = "ARTIST"; - assert!(ApeItem::new( - String::from(INVALID_KEY_ONE_CHARACTER), - ItemValue::Text(String::from("invalid key: one character")) - ) - .is_err()); - assert!(ApeItem::new( - String::from(INVALID_KEY_FORBIDDEN_STRING), - ItemValue::Text(String::from("invalid key: forbidden string")) - ) - .is_err()); - assert!(ApeItem::new( - String::from(INVALID_KEY_UNICODE), - ItemValue::Text(String::from("invalid key: Unicode")) - ) - .is_err()); + assert!( + ApeItem::new( + String::from(INVALID_KEY_ONE_CHARACTER), + ItemValue::Text(String::from("invalid key: one character")) + ) + .is_err() + ); + assert!( + ApeItem::new( + String::from(INVALID_KEY_FORBIDDEN_STRING), + ItemValue::Text(String::from("invalid key: forbidden string")) + ) + .is_err() + ); + assert!( + ApeItem::new( + String::from(INVALID_KEY_UNICODE), + ItemValue::Text(String::from("invalid key: Unicode")) + ) + .is_err() + ); let valid_space_and_tilde = ApeItem::new( String::from(VALID_KEY_SPACE_AND_TILDE), @@ -84,7 +90,7 @@ fn test_invalid_keys() { assert_eq!(tag.len(), 2); } -#[test] +#[test_log::test] #[ignore] fn test_text_binary() { // Marker test, this is useless as Lofty does not have a similar API that the test is based upon: @@ -92,7 +98,7 @@ fn test_text_binary() { } // TODO: Does not work! We fall for this collision. -#[test] +#[test_log::test] #[ignore] fn test_id3v1_collision() { let mut file = temp_file!("tests/taglib/data/no-tags.mpc"); diff --git a/lofty/tests/taglib/test_fileref.rs b/lofty/tests/taglib/test_fileref.rs index 5e888580..537eaabf 100644 --- a/lofty/tests/taglib/test_fileref.rs +++ b/lofty/tests/taglib/test_fileref.rs @@ -75,109 +75,109 @@ fn file_ref_save(path: &str, expected_file_type: FileType) { // no need to replicate these. } -#[test] +#[test_log::test] fn test_musepack() { file_ref_save("click.mpc", FileType::Mpc); } -#[test] +#[test_log::test] #[ignore] fn test_asf() { // TODO: We don't support ASF yet // file_ref_save("silence-1.asf", FileType::ASF); } -#[test] +#[test_log::test] fn test_vorbis() { file_ref_save("empty.ogg", FileType::Vorbis); } -#[test] +#[test_log::test] fn test_speex() { file_ref_save("empty.spx", FileType::Speex); } -#[test] +#[test_log::test] fn test_flac() { file_ref_save("no-tags.flac", FileType::Flac); } -#[test] +#[test_log::test] fn test_mp3() { file_ref_save("xing.mp3", FileType::Mpeg); } -#[test] +#[test_log::test] #[ignore] fn test_true_audio() { // TODO: We don't support TTA yet // file_ref_save("empty.tta", FileType::TrueAudio); } -#[test] +#[test_log::test] fn test_mp4_1() { file_ref_save("has-tags.m4a", FileType::Mp4); } -#[test] +#[test_log::test] #[ignore] // TODO: The file has a malformed `free` atom. How does TagLib handle this? Currently we mess up entirely and just write a duplicate tag. fn test_mp4_2() { file_ref_save("no-tags.m4a", FileType::Mp4); } -#[test] +#[test_log::test] #[ignore] // TODO: We are able to write the first tag and even reread, but the second save causes a `SizeMismatch`. fn test_mp4_3() { file_ref_save("no-tags.3g2", FileType::Mp4); } -#[test] +#[test_log::test] fn test_mp4_4() { file_ref_save("blank_video.m4v", FileType::Mp4); } -#[test] +#[test_log::test] fn test_wav() { file_ref_save("empty.wav", FileType::Wav); } -#[test] +#[test_log::test] #[ignore] // TODO: We don't yet support FLAC in oga fn test_oga_flac() { file_ref_save("empty_flac.oga", FileType::Flac); } -#[test] +#[test_log::test] fn test_oga_vorbis() { file_ref_save("empty_vorbis.oga", FileType::Vorbis); } -#[test] +#[test_log::test] fn test_ape() { file_ref_save("mac-399.ape", FileType::Ape); } -#[test] +#[test_log::test] fn test_aiff_1() { file_ref_save("empty.aiff", FileType::Aiff); } -#[test] +#[test_log::test] fn test_aiff_2() { file_ref_save("alaw.aifc", FileType::Aiff); } -#[test] +#[test_log::test] fn test_wavpack() { file_ref_save("click.wv", FileType::WavPack); } -#[test] +#[test_log::test] fn test_opus() { file_ref_save("correctness_gain_silent_output.opus", FileType::Opus); } -#[test] +#[test_log::test] fn test_unsupported() { let f1 = lofty::read_from_path("tests/taglib/data/no-extension"); match f1 { @@ -192,13 +192,13 @@ fn test_unsupported() { } } -#[test] +#[test_log::test] #[ignore] fn test_create() { // Marker test, Lofty does not replicate this API } -#[test] +#[test_log::test] fn test_audio_properties() { let file = lofty::read_from_path("tests/taglib/data/xing.mp3").unwrap(); let properties = file.properties(); @@ -206,7 +206,7 @@ fn test_audio_properties() { assert_eq!(properties.duration().as_millis(), 2064); } -#[test] +#[test_log::test] #[ignore] fn test_default_file_extensions() { // Marker test, Lofty does not replicate this API @@ -217,7 +217,7 @@ use lofty::properties::FileProperties; use rusty_fork::rusty_fork_test; rusty_fork_test! { - #[test] + #[test_log::test] fn test_file_resolver() { lofty::config::apply_global_options(GlobalOptions::new().use_custom_resolvers(true)); diff --git a/lofty/tests/taglib/test_flac.rs b/lofty/tests/taglib/test_flac.rs index e21ead32..696c228e 100644 --- a/lofty/tests/taglib/test_flac.rs +++ b/lofty/tests/taglib/test_flac.rs @@ -11,7 +11,7 @@ use lofty::ogg::{OggPictureStorage, VorbisComments}; use lofty::picture::{MimeType, Picture, PictureInformation, PictureType}; use lofty::tag::{Accessor, TagExt}; -#[test] +#[test_log::test] fn test_signature() { let f = get_file::("tests/taglib/data/no-tags.flac"); assert_eq!( @@ -20,7 +20,7 @@ fn test_signature() { ); } -#[test] +#[test_log::test] #[ignore] fn test_multiple_comment_blocks() { // Marker test, Lofty does not replicate TagLib's behavior @@ -28,7 +28,7 @@ fn test_multiple_comment_blocks() { // TagLib will use the *first* tag in the stream, while we use the latest. } -#[test] +#[test_log::test] fn test_read_picture() { let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); let f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); @@ -46,7 +46,7 @@ fn test_read_picture() { assert_eq!(pic.data().len(), 150); } -#[test] +#[test_log::test] fn test_add_picture() { let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); { @@ -102,7 +102,7 @@ fn test_add_picture() { } } -#[test] +#[test_log::test] fn test_replace_picture() { let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); { @@ -149,7 +149,7 @@ fn test_replace_picture() { } } -#[test] +#[test_log::test] fn test_remove_all_pictures() { let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); { @@ -172,7 +172,7 @@ fn test_remove_all_pictures() { } } -#[test] +#[test_log::test] fn test_repeated_save_1() { let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); { @@ -214,14 +214,14 @@ fn test_repeated_save_1() { } } -#[test] +#[test_log::test] #[ignore] fn test_repeated_save_2() { // Marker test, this test relies on saving an ID3v2 tag in a FLAC file, something Lofty does not and will not support. } // TODO: We don't make use of padding blocks yet -#[test] +#[test_log::test] #[ignore] fn test_repeated_save_3() { let mut file = temp_file!("tests/taglib/data/no-tags.flac"); @@ -240,7 +240,7 @@ fn test_repeated_save_3() { assert_eq!(file.metadata().unwrap().len(), 12862); } -#[test] +#[test_log::test] fn test_save_multiple_values() { let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); { @@ -265,13 +265,13 @@ fn test_save_multiple_values() { } } -#[test] +#[test_log::test] #[ignore] fn test_dict() { // Marker test, Lofty does not replicate the dict API } -#[test] +#[test_log::test] fn test_properties() { let mut tag = VorbisComments::default(); tag.push(String::from("ALBUM"), String::from("Album")); @@ -361,7 +361,7 @@ fn test_properties() { } } -#[test] +#[test_log::test] fn test_invalid() { let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); let mut f = FlacFile::read_from(&mut file, ParseOptions::new()).unwrap(); @@ -375,7 +375,7 @@ fn test_invalid() { assert!(f.vorbis_comments().unwrap().is_empty()); } -#[test] +#[test_log::test] fn test_audio_properties() { let f = get_file::("tests/taglib/data/sinewave.flac"); @@ -394,12 +394,12 @@ fn test_audio_properties() { ); } -#[test] +#[test_log::test] fn test_zero_sized_padding_1() { let _f = get_file::("tests/taglib/data/zero-sized-padding.flac"); } -#[test] +#[test_log::test] fn test_zero_sized_padding_2() { let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); { @@ -428,7 +428,7 @@ fn test_zero_sized_padding_2() { } // TODO: We don't make use of padding blocks yet -#[test] +#[test_log::test] #[ignore] fn test_shrink_padding() { let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); @@ -455,19 +455,19 @@ fn test_shrink_padding() { } } -#[test] +#[test_log::test] #[ignore] fn test_save_id3v1() { // Marker test, this test relies on saving an ID3v1 tag in a FLAC file, something Lofty does not and will not support. } -#[test] +#[test_log::test] #[ignore] fn test_update_id3v2() { // Marker test, this test relies on saving an ID3v2 tag in a FLAC file, something Lofty does not and will not support. } -#[test] +#[test_log::test] fn test_empty_id3v2() { let mut file = temp_file!("tests/taglib/data/no-tags.flac"); { @@ -485,7 +485,7 @@ fn test_empty_id3v2() { } // TODO: TagLib doesn't fully remove Vorbis Comments when stripping. It will preserve the vendor string. Should we do the same? -#[test] +#[test_log::test] #[ignore] fn test_strip_tags() { // NOTE: In the TagLib test suite, this also tests ID3v1 and ID3v2. That is not replicated here. @@ -527,7 +527,7 @@ fn test_strip_tags() { } } -#[test] +#[test_log::test] fn test_remove_xiph_field() { let mut file = temp_file!("tests/taglib/data/silence-44-s.flac"); { @@ -560,7 +560,7 @@ fn test_remove_xiph_field() { } } -#[test] +#[test_log::test] fn test_empty_seek_table() { let mut file = temp_file!("tests/taglib/data/empty-seektable.flac"); { @@ -584,7 +584,7 @@ fn test_empty_seek_table() { } } -#[test] +#[test_log::test] fn test_picture_stored_after_comment() { // Blank.png from https://commons.wikimedia.org/wiki/File:Blank.png const BLANK_PNG_DATA: &[u8] = &[ diff --git a/lofty/tests/taglib/test_flacpicture.rs b/lofty/tests/taglib/test_flacpicture.rs index 981f55d4..41a5ea65 100644 --- a/lofty/tests/taglib/test_flacpicture.rs +++ b/lofty/tests/taglib/test_flacpicture.rs @@ -17,7 +17,7 @@ const DATA: &[u8] = &[ 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82, ]; -#[test] +#[test_log::test] fn test_parse() { let (picture, info) = Picture::from_flac_bytes(DATA, false, ParsingMode::Strict).unwrap(); @@ -31,7 +31,7 @@ fn test_parse() { assert_eq!(picture.data().len(), 150); } -#[test] +#[test_log::test] fn test_pass_through() { let (picture, info) = Picture::from_flac_bytes(DATA, false, ParsingMode::Strict).unwrap(); assert_eq!(DATA, picture.as_flac_bytes(info, false).as_slice()); diff --git a/lofty/tests/taglib/test_id3v1.rs b/lofty/tests/taglib/test_id3v1.rs index b9016b23..8255b359 100644 --- a/lofty/tests/taglib/test_id3v1.rs +++ b/lofty/tests/taglib/test_id3v1.rs @@ -1,12 +1,12 @@ use lofty::id3::v1::GENRES; -#[test] +#[test_log::test] #[ignore] fn test_strip_whitespace() { // Marker test, we'd be overstepping to remove trailing whitespace that may be intentional } -#[test] +#[test_log::test] fn test_genres() { assert_eq!("Darkwave", GENRES[50]); assert_eq!( @@ -23,7 +23,7 @@ fn test_genres() { ); } -#[test] +#[test_log::test] #[ignore] fn test_renamed_genres() { // Marker test, this covers a change where TagLib deviated from the list of genres available on Wikipedia. diff --git a/lofty/tests/taglib/test_id3v2.rs b/lofty/tests/taglib/test_id3v2.rs index 4c4fce44..b3fbe8df 100644 --- a/lofty/tests/taglib/test_id3v2.rs +++ b/lofty/tests/taglib/test_id3v2.rs @@ -4,6 +4,7 @@ use std::borrow::Cow; use std::collections::HashMap; use std::io::{Read, Seek}; +use lofty::TextEncoding; use lofty::config::{ParseOptions, ParsingMode, WriteOptions}; use lofty::file::AudioFile; use lofty::id3::v2::{ @@ -18,9 +19,8 @@ use lofty::mpeg::MpegFile; use lofty::picture::{MimeType, Picture, PictureType}; use lofty::tag::items::Timestamp; use lofty::tag::{Accessor, TagExt}; -use lofty::TextEncoding; -#[test] +#[test_log::test] fn test_unsynch_decode() { let mut file = temp_file!("tests/taglib/data/unsynch.id3"); let f = MpegFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); @@ -32,7 +32,7 @@ fn test_unsynch_decode() { ); } -#[test] +#[test_log::test] fn test_downgrade_utf8_for_id3v23_1() { let mut file = temp_file!("tests/taglib/data/xing.mp3"); @@ -64,7 +64,7 @@ fn test_downgrade_utf8_for_id3v23_1() { assert_eq!(f2.encoding, TextEncoding::UTF16); } -#[test] +#[test_log::test] fn test_downgrade_utf8_for_id3v23_2() { let mut file = temp_file!("tests/taglib/data/xing.mp3"); @@ -93,7 +93,7 @@ fn test_downgrade_utf8_for_id3v23_2() { assert_eq!(f2.encoding, TextEncoding::UTF16); } -#[test] +#[test_log::test] fn test_utf16be_delimiter() { let mut f = TextInformationFrame::new( FrameId::Valid(Cow::Borrowed("TIT2")), @@ -119,7 +119,7 @@ fn test_utf16be_delimiter() { assert_eq!(f.value, "Foo\0Bar"); } -#[test] +#[test_log::test] fn test_utf16_delimiter() { let mut f = TextInformationFrame::new( FrameId::Valid(Cow::Borrowed("TIT2")), @@ -187,7 +187,7 @@ fn test_utf16_delimiter() { assert_eq!(f.value, "Foo\0Bar"); } -#[test] +#[test_log::test] #[ignore] fn test_broken_frame1() { // TODO: Determine if it is worth supporting unsychronized frame sizes in ID3v2.4 @@ -201,13 +201,13 @@ fn test_broken_frame1() { // .contains(&FrameId::Valid(Cow::from("TENC")))); } -#[test] +#[test_log::test] #[ignore] fn test_read_string_field() { // Marker test, this is not an API Lofty replicates } -#[test] +#[test_log::test] fn test_parse_apic() { let f = AttachedPictureFrame::parse( &mut &b"\ @@ -228,7 +228,7 @@ fn test_parse_apic() { assert_eq!(f.picture.description(), Some("d")); } -#[test] +#[test_log::test] fn test_parse_apic_utf16_bom() { let f = AttachedPictureFrame::parse( &mut &b"\ @@ -246,7 +246,7 @@ fn test_parse_apic_utf16_bom() { assert_eq!(f.picture.data(), b"\xff\xd8\xff"); } -#[test] +#[test_log::test] fn test_parse_apicv22() { let frame = AttachedPictureFrame::parse( &mut &b"\ @@ -265,7 +265,7 @@ fn test_parse_apicv22() { assert_eq!(frame.picture.description(), Some("d")); } -#[test] +#[test_log::test] fn test_render_apic() { let f = AttachedPictureFrame::new( TextEncoding::UTF8, @@ -288,13 +288,13 @@ fn test_render_apic() { ); } -#[test] +#[test_log::test] #[ignore] fn test_dont_render22() { // Marker test, not sure what's going on here? } -#[test] +#[test_log::test] fn test_parse_geob() { let f = GeneralEncapsulatedObject::parse( b"\ @@ -311,7 +311,7 @@ fn test_parse_geob() { assert_eq!(f.descriptor.as_deref(), Some("d")); } -#[test] +#[test_log::test] fn test_render_geob() { let f = GeneralEncapsulatedObject::new( TextEncoding::Latin1, @@ -332,7 +332,7 @@ fn test_render_geob() { ); } -#[test] +#[test_log::test] fn test_parse_popm() { let f = PopularimeterFrame::parse( &mut &b"\ @@ -347,7 +347,7 @@ fn test_parse_popm() { assert_eq!(f.counter, 3); } -#[test] +#[test_log::test] fn test_parse_popm_without_counter() { let f = PopularimeterFrame::parse( &mut &b"\ @@ -361,7 +361,7 @@ fn test_parse_popm_without_counter() { assert_eq!(f.counter, 0); } -#[test] +#[test_log::test] fn test_render_popm() { let f = PopularimeterFrame::new(String::from("email@example.com"), 2, 3); @@ -374,13 +374,13 @@ fn test_render_popm() { ); } -#[test] +#[test_log::test] #[ignore] fn test_popm_to_string() { // Marker test, Lofty doesn't have a display impl for Popularimeter } -#[test] +#[test_log::test] fn test_popm_from_file() { let mut file = temp_file!("tests/taglib/data/xing.mp3"); @@ -412,7 +412,7 @@ fn test_popm_from_file() { } } -#[test] +#[test_log::test] #[allow(clippy::float_cmp)] fn test_parse_relative_volume_frame() { let f = RelativeVolumeAdjustmentFrame::parse( @@ -441,7 +441,7 @@ fn test_parse_relative_volume_frame() { assert_eq!(channels.len(), 1); } -#[test] +#[test_log::test] fn test_render_relative_volume_frame() { let f = RelativeVolumeAdjustmentFrame::new(String::from("ident"), { let mut m = HashMap::new(); @@ -468,7 +468,7 @@ fn test_render_relative_volume_frame() { ); } -#[test] +#[test_log::test] fn test_parse_unique_file_identifier_frame() { let f = UniqueFileIdentifierFrame::parse( &mut &b"\ @@ -484,7 +484,7 @@ fn test_parse_unique_file_identifier_frame() { assert_eq!(f.identifier, &[0x00, 0x01, 0x02]); } -#[test] +#[test_log::test] fn test_parse_empty_unique_file_identifier_frame() { let f = UniqueFileIdentifierFrame::parse( &mut &b"\ @@ -498,7 +498,7 @@ fn test_parse_empty_unique_file_identifier_frame() { assert!(f.is_err()); } -#[test] +#[test_log::test] fn test_render_unique_file_identifier_frame() { let f = UniqueFileIdentifierFrame::new(String::from("owner"), b"\x01\x02\x03".to_vec()); @@ -510,7 +510,7 @@ owner\x00\ ); } -#[test] +#[test_log::test] fn test_parse_url_link_frame() { let f = UrlLinkFrame::parse( &mut &b"http://example.com"[..], @@ -522,7 +522,7 @@ fn test_parse_url_link_frame() { assert_eq!(f.url(), "http://example.com"); } -#[test] +#[test_log::test] fn test_render_url_link_frame() { let f = UrlLinkFrame::parse( &mut &b"http://example.com"[..], @@ -534,7 +534,7 @@ fn test_render_url_link_frame() { assert_eq!(f.as_bytes(), b"http://example.com"); } -#[test] +#[test_log::test] fn test_parse_user_url_link_frame() { let f = ExtendedUrlFrame::parse( &mut &b"\ @@ -551,7 +551,7 @@ fn test_parse_user_url_link_frame() { assert_eq!(f.content, String::from("http://example.com")); } -#[test] +#[test_log::test] fn test_render_user_url_link_frame() { let f = ExtendedUrlFrame::new( TextEncoding::Latin1, @@ -568,7 +568,7 @@ fn test_render_user_url_link_frame() { ); } -#[test] +#[test_log::test] fn test_parse_ownership_frame() { let f = OwnershipFrame::parse( &mut &b"\ @@ -586,7 +586,7 @@ fn test_parse_ownership_frame() { assert_eq!(f.seller, "Beatport"); } -#[test] +#[test_log::test] fn test_render_ownership_frame() { let f = OwnershipFrame::new( TextEncoding::Latin1, @@ -605,7 +605,7 @@ fn test_render_ownership_frame() { ) } -#[test] +#[test_log::test] fn test_parse_synchronized_lyrics_frame() { let f = SynchronizedTextFrame::parse( b"\ @@ -635,7 +635,7 @@ Lyrics\x00\ assert_eq!(f.content[1].0, 4567); } -#[test] +#[test_log::test] fn test_parse_synchronized_lyrics_frame_with_empty_description() { let f = SynchronizedTextFrame::parse( b"\ @@ -665,7 +665,7 @@ fn test_parse_synchronized_lyrics_frame_with_empty_description() { assert_eq!(f.content[1].0, 4567); } -#[test] +#[test_log::test] fn test_render_synchronized_lyrics_frame() { let f = SynchronizedTextFrame::new( TextEncoding::Latin1, @@ -694,7 +694,7 @@ fn test_render_synchronized_lyrics_frame() { ); } -#[test] +#[test_log::test] fn test_parse_event_timing_codes_frame() { let f = EventTimingCodesFrame::parse( &mut &b"\ @@ -718,7 +718,7 @@ fn test_parse_event_timing_codes_frame() { assert_eq!(sel[1].timestamp, 3_600_000); } -#[test] +#[test_log::test] fn test_render_event_timing_codes_frame() { let f = EventTimingCodesFrame::new( TimestampFormat::MS, @@ -745,7 +745,7 @@ fn test_render_event_timing_codes_frame() { ) } -#[test] +#[test_log::test] fn test_parse_comments_frame() { let f = CommentFrame::parse( &mut &b"\x03\ @@ -764,7 +764,7 @@ fn test_parse_comments_frame() { assert_eq!(f.content, String::from("Text")); } -#[test] +#[test_log::test] fn test_render_comments_frame() { let f = CommentFrame::new( TextEncoding::UTF16, @@ -783,19 +783,19 @@ fn test_render_comments_frame() { ); } -#[test] +#[test_log::test] #[ignore] fn test_parse_podcast_frame() { // Marker test, Lofty doesn't have dedicated support for PCST frames, it seems unnecessary } -#[test] +#[test_log::test] #[ignore] fn test_render_podcast_frame() { // Marker test, Lofty doesn't have dedicated support for PCST frames, it seems unnecessary } -#[test] +#[test_log::test] fn test_parse_private_frame() { let f = PrivateFrame::parse( &mut &b"\ @@ -810,7 +810,7 @@ fn test_parse_private_frame() { assert_eq!(f.private_data, b"TL"); } -#[test] +#[test_log::test] fn test_render_private_frame() { let f = PrivateFrame::new(String::from("WM/Provider"), b"TL".to_vec()); @@ -822,7 +822,7 @@ fn test_render_private_frame() { ); } -#[test] +#[test_log::test] fn test_parse_user_text_identification_frame() { let frame_without_description = ExtendedUrlFrame::parse( &mut &b"\ @@ -855,7 +855,7 @@ fn test_parse_user_text_identification_frame() { assert_eq!(frame_with_description.content, String::from("Text")); } -#[test] +#[test_log::test] fn test_render_user_text_identification_frame() { let mut f = ExtendedTextFrame::new(TextEncoding::Latin1, String::new(), String::from("Text")); @@ -879,16 +879,17 @@ fn test_render_user_text_identification_frame() { } // TODO: iTunes, being the great application it is writes unsynchronized integers for sizes. There's no *great* way to detect this. -#[test] +#[test_log::test] #[ignore] fn test_itunes_24_frame_size() { let mut file = temp_file!("tests/taglib/data/005411.id3"); let f = MpegFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); - assert!(f - .id3v2() - .unwrap() - .contains(&FrameId::Valid(Cow::from("TIT2")))); + assert!( + f.id3v2() + .unwrap() + .contains(&FrameId::Valid(Cow::from("TIT2"))) + ); assert_eq!( f.id3v2() .unwrap() @@ -898,7 +899,7 @@ fn test_itunes_24_frame_size() { ); } -#[test] +#[test_log::test] fn test_save_utf16_comment() { let mut file = temp_file!("tests/taglib/data/xing.mp3"); @@ -929,7 +930,7 @@ fn test_save_utf16_comment() { // TODO: Probably won't ever support this, it's a weird edge case with // duplicate genres. That can be up to the caller to figure out. -#[test] +#[test_log::test] #[ignore] fn test_update_genre_23_1() { // "Refinement" is the same as the ID3v1 genre - duplicate @@ -953,7 +954,7 @@ fn test_update_genre_23_1() { assert_eq!(tag.genre().as_deref(), Some("Death Metal")); } -#[test] +#[test_log::test] fn test_update_genre23_2() { // "Refinement" is different from the ID3v1 genre let frame_value = TextInformationFrame::parse( @@ -977,7 +978,7 @@ fn test_update_genre23_2() { assert_eq!(tag.genre().as_deref(), Some("Disco / Eurodisco")); } -#[test] +#[test_log::test] fn test_update_genre23_3() { // Multiple references and a refinement let frame_value = TextInformationFrame::parse( @@ -1005,7 +1006,7 @@ fn test_update_genre23_3() { ); } -#[test] +#[test_log::test] fn test_update_genre_24() { let frame_value = TextInformationFrame::parse( &mut &b"\x00\ @@ -1023,7 +1024,7 @@ fn test_update_genre_24() { assert_eq!(tag.genre().as_deref(), Some("R&B / Eurodisco")); } -#[test] +#[test_log::test] fn test_update_date22() { let mut file = temp_file!("tests/taglib/data/id3v22-tda.mp3"); let f = MpegFile::read_from(&mut file, ParseOptions::new()).unwrap(); @@ -1032,7 +1033,7 @@ fn test_update_date22() { } // TODO: Determine if this is even worth doing. It is just combining TYE+TDA when upgrading ID3v2.2 to 2.4 -#[test] +#[test_log::test] #[ignore] fn test_update_full_date22() { let mut file = temp_file!("tests/taglib/data/id3v22-tda.mp3"); @@ -1047,7 +1048,7 @@ fn test_update_full_date22() { ); } -#[test] +#[test_log::test] fn test_downgrade_to_23() { let mut file = temp_file!("tests/taglib/data/xing.mp3"); @@ -1238,14 +1239,15 @@ fn test_downgrade_to_23() { } } -#[test] +#[test_log::test] fn test_compressed_frame_with_broken_length() { let mut file = temp_file!("tests/taglib/data/compressed_id3_frame.mp3"); let f = MpegFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); - assert!(f - .id3v2() - .unwrap() - .contains(&FrameId::Valid(Cow::from("APIC")))); + assert!( + f.id3v2() + .unwrap() + .contains(&FrameId::Valid(Cow::from("APIC"))) + ); let frame = f .id3v2() @@ -1262,15 +1264,16 @@ fn test_compressed_frame_with_broken_length() { assert_eq!(picture.data().len(), 86414); } -#[test] +#[test_log::test] fn test_w000() { let mut file = temp_file!("tests/taglib/data/w000.mp3"); let f = MpegFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); - assert!(f - .id3v2() - .unwrap() - .contains(&FrameId::Valid(Cow::from("W000")))); + assert!( + f.id3v2() + .unwrap() + .contains(&FrameId::Valid(Cow::from("W000"))) + ); let frame = f .id3v2() .unwrap() @@ -1282,33 +1285,33 @@ fn test_w000() { assert_eq!(url_frame.url(), "lukas.lalinsky@example.com____"); } -#[test] +#[test_log::test] #[ignore] fn test_property_interface() { // Marker test, Lofty does not replicate the property interface } -#[test] +#[test_log::test] #[ignore] fn test_property_interface2() { // Marker test, Lofty does not replicate the property interface } -#[test] +#[test_log::test] #[ignore] fn test_properties_movement() { // Marker test, Lofty does not replicate the property interface. // Outside of that, this is simply a text frame parsing test, which is redundant. } -#[test] +#[test_log::test] #[ignore] fn test_property_grouping() { // Marker test, Lofty does not replicate the property interface. // Outside of that, this is simply a text frame parsing test, which is redundant. } -#[test] +#[test_log::test] fn test_delete_frame() { let mut file = temp_file!("tests/taglib/data/rare_frames.mp3"); @@ -1328,7 +1331,7 @@ fn test_delete_frame() { } } -#[test] +#[test_log::test] fn test_save_and_strip_id3v1_should_not_add_frame_from_id3v1_to_id3v2() { let mut file = temp_file!("tests/taglib/data/xing.mp3"); @@ -1360,38 +1363,38 @@ fn test_save_and_strip_id3v1_should_not_add_frame_from_id3v1_to_id3v2() { } // TODO: Support CHAP frames (#189) -#[test] +#[test_log::test] #[ignore] fn test_parse_chapter_frame() {} // TODO: Support CHAP frames (#189) -#[test] +#[test_log::test] #[ignore] fn test_render_chapter_frame() {} // TODO: Support CTOC frames (#189) -#[test] +#[test_log::test] #[ignore] fn test_parse_table_of_contents_frame() {} // TODO: Support CTOC frames (#189) -#[test] +#[test_log::test] #[ignore] fn test_render_table_of_contents_frame() {} -#[test] +#[test_log::test] #[ignore] fn test_empty_frame() { // Marker test, Lofty will not remove empty frames, as they can be valid } -#[test] +#[test_log::test] #[ignore] fn test_duplicate_tags() { // Marker test, Lofty will combine duplicated tags } // TODO: Support CTOC frames (#189) -#[test] +#[test_log::test] #[ignore] fn test_parse_toc_frame_with_many_children() {} diff --git a/lofty/tests/taglib/test_info.rs b/lofty/tests/taglib/test_info.rs index fbda110e..a123806c 100644 --- a/lofty/tests/taglib/test_info.rs +++ b/lofty/tests/taglib/test_info.rs @@ -1,7 +1,7 @@ use lofty::iff::wav::RiffInfoList; use lofty::tag::Accessor; -#[test] +#[test_log::test] fn test_title() { let mut tag = RiffInfoList::default(); @@ -14,7 +14,7 @@ fn test_title() { assert_eq!(tag.get("TEST"), Some("Dummy Text")); } -#[test] +#[test_log::test] fn test_numeric_fields() { let mut tag = RiffInfoList::default(); diff --git a/lofty/tests/taglib/test_mp4.rs b/lofty/tests/taglib/test_mp4.rs index 00f43fb3..ebfcd492 100644 --- a/lofty/tests/taglib/test_mp4.rs +++ b/lofty/tests/taglib/test_mp4.rs @@ -10,7 +10,7 @@ use lofty::mp4::{Atom, AtomData, AtomIdent, Ilst, Mp4Codec, Mp4File}; use lofty::picture::{MimeType, Picture, PictureType}; use lofty::tag::{Accessor, TagExt, TagType}; -#[test] +#[test_log::test] fn test_properties_aac() { let f = get_file::("tests/taglib/data/has-tags.m4a"); assert_eq!(f.properties().duration().as_secs(), 3); @@ -24,7 +24,7 @@ fn test_properties_aac() { assert_eq!(f.properties().codec(), &Mp4Codec::AAC); } -#[test] +#[test_log::test] #[allow(clippy::needless_range_loop)] fn test_properties_aac_without_bitrate() { let mut file = temp_file!("tests/taglib/data/has-tags.m4a"); @@ -49,7 +49,7 @@ fn test_properties_aac_without_bitrate() { assert_eq!(f.properties().codec(), &Mp4Codec::AAC); } -#[test] +#[test_log::test] fn test_properties_alac() { let f = get_file::("tests/taglib/data/empty_alac.m4a"); assert_eq!(f.properties().duration().as_secs(), 3); @@ -62,7 +62,7 @@ fn test_properties_alac() { assert_eq!(f.properties().codec(), &Mp4Codec::ALAC); } -#[test] +#[test_log::test] #[allow(clippy::needless_range_loop)] fn test_properties_alac_without_bitrate() { let mut file = temp_file!("tests/taglib/data/empty_alac.m4a"); @@ -87,7 +87,7 @@ fn test_properties_alac_without_bitrate() { assert_eq!(f.properties().codec(), &Mp4Codec::ALAC); } -#[test] +#[test_log::test] fn test_properties_m4v() { let f = get_file::("tests/taglib/data/blank_video.m4v"); assert_eq!(f.properties().duration().as_secs(), 0); @@ -100,13 +100,13 @@ fn test_properties_m4v() { assert_eq!(f.properties().codec(), &Mp4Codec::AAC); } -#[test] +#[test_log::test] fn test_check_valid() { let mut file = temp_file!("tests/taglib/data/empty.aiff"); assert!(Mp4File::read_from(&mut file, ParseOptions::new()).is_err()); } -#[test] +#[test_log::test] fn test_has_tag() { { let f = get_file::("tests/taglib/data/has-tags.m4a"); @@ -132,7 +132,7 @@ fn test_has_tag() { } } -#[test] +#[test_log::test] fn test_is_empty() { let mut t1 = Ilst::default(); assert!(t1.is_empty()); @@ -140,7 +140,7 @@ fn test_is_empty() { assert!(!t1.is_empty()); } -#[test] +#[test_log::test] #[ignore] // TODO: The atom parsing internals are not exposed yet fn test_update_stco() { let mut file = temp_file!("no-tags.3g2"); @@ -165,7 +165,7 @@ fn test_update_stco() { } } -#[test] +#[test_log::test] fn test_freeform() { let mut file = temp_file!("tests/taglib/data/has-tags.m4a"); @@ -211,7 +211,7 @@ fn test_freeform() { } } -#[test] +#[test_log::test] fn test_save_existing_when_ilst_is_last() { let mut file = temp_file!("tests/taglib/data/ilst-is-last.m4a"); @@ -257,19 +257,19 @@ fn test_save_existing_when_ilst_is_last() { } } -#[test] +#[test_log::test] #[ignore] fn test_64bit_atom() { // Marker test, this just checks the moov atom's length. We don't retain any atoms we don't need. } -#[test] +#[test_log::test] fn test_gnre() { let f = get_file::("tests/taglib/data/gnre.m4a"); assert_eq!(f.ilst().unwrap().genre().as_deref(), Some("Ska")); } -#[test] +#[test_log::test] fn test_covr_read() { let f = get_file::("tests/taglib/data/has-tags.m4a"); let tag = f.ilst().unwrap(); @@ -289,7 +289,7 @@ fn test_covr_read() { assert_eq!(picture2.data().len(), 287); } -#[test] +#[test_log::test] fn test_covr_write() { let mut file = temp_file!("tests/taglib/data/has-tags.m4a"); @@ -334,7 +334,7 @@ fn test_covr_write() { } } -#[test] +#[test_log::test] fn test_covr_read2() { let f = get_file::("tests/taglib/data/covr-junk.m4a"); let tag = f.ilst().unwrap(); @@ -354,30 +354,30 @@ fn test_covr_read2() { assert_eq!(picture2.data().len(), 287); } -#[test] +#[test_log::test] #[ignore] fn test_properties() { // Marker test, Lofty does not replicate the properties API } -#[test] +#[test_log::test] #[ignore] fn test_properties_all_supported() { // Marker test, Lofty does not replicate the properties API } -#[test] +#[test_log::test] #[ignore] fn test_properties_movement() { // Marker test, Lofty does not replicate the properties API } -#[test] +#[test_log::test] fn test_fuzzed_file() { let _f = get_file::("tests/taglib/data/infloop.m4a"); } -#[test] +#[test_log::test] fn test_repeated_save() { let mut file = temp_file!("tests/taglib/data/no-tags.m4a"); let mut f = Mp4File::read_from(&mut file, ParseOptions::new()).unwrap(); @@ -404,20 +404,20 @@ fn test_repeated_save() { assert_ne!(file_bytes.get(2863..2873), Some(b"0123456789".as_slice())); } -#[test] +#[test_log::test] fn test_with_zero_length_atom() { let f = get_file::("tests/taglib/data/zero-length-mdat.m4a"); assert_eq!(f.properties().duration().as_millis(), 1115); assert_eq!(f.properties().sample_rate(), 22050); } -#[test] +#[test_log::test] #[ignore] fn test_empty_values_remove_items() { // Marker test, Lofty treats empty values as valid } -#[test] +#[test_log::test] fn test_remove_metadata() { let mut file = temp_file!("tests/taglib/data/no-tags.m4a"); @@ -467,7 +467,7 @@ fn test_remove_metadata() { } } -#[test] +#[test_log::test] fn test_non_full_meta_atom() { let f = get_file::("tests/taglib/data/non-full-meta.m4a"); assert!(f.ilst().is_some()); diff --git a/lofty/tests/taglib/test_mpc.rs b/lofty/tests/taglib/test_mpc.rs index 928fbb2b..00d4e25a 100644 --- a/lofty/tests/taglib/test_mpc.rs +++ b/lofty/tests/taglib/test_mpc.rs @@ -11,7 +11,7 @@ use lofty::musepack::{MpcFile, MpcProperties}; use lofty::probe::Probe; use lofty::tag::{Accessor, TagExt}; -#[test] +#[test_log::test] fn test_properties_sv8() { let f = get_file::("tests/taglib/data/sv8_header.mpc"); @@ -30,7 +30,7 @@ fn test_properties_sv8() { // assert_eq!(properties.sample_frames(), 66014); } -#[test] +#[test_log::test] fn test_properties_sv7() { let f = get_file::("tests/taglib/data/click.mpc"); @@ -56,18 +56,18 @@ fn test_properties_sv7() { assert_eq!(properties.album_peak(), 19848); } -#[test] +#[test_log::test] fn test_properties_sv5() { // Marker test, TagLib doesn't seem to produce the correct properties for SV5 } -#[test] +#[test_log::test] #[ignore] fn test_properties_sv4() { // Marker test, TagLib doesn't seem to produce the correct properties for SV4 } -#[test] +#[test_log::test] fn test_fuzzed_file1() { let _ = Probe::open("tests/taglib/data/zerodiv.mpc") .unwrap() @@ -75,7 +75,7 @@ fn test_fuzzed_file1() { .unwrap(); } -#[test] +#[test_log::test] fn test_fuzzed_file2() { let _ = Probe::open("tests/taglib/data/infloop.mpc") .unwrap() @@ -83,7 +83,7 @@ fn test_fuzzed_file2() { .unwrap(); } -#[test] +#[test_log::test] fn test_fuzzed_file3() { let _ = Probe::open("tests/taglib/data/segfault.mpc") .unwrap() @@ -91,7 +91,7 @@ fn test_fuzzed_file3() { .unwrap(); } -#[test] +#[test_log::test] fn test_fuzzed_file4() { let _ = Probe::open("tests/taglib/data/segfault2.mpc") .unwrap() @@ -99,7 +99,7 @@ fn test_fuzzed_file4() { .unwrap(); } -#[test] +#[test_log::test] fn test_strip_and_properties() { let mut file = temp_file!("tests/taglib/data/click.mpc"); @@ -136,7 +136,7 @@ fn test_strip_and_properties() { } } -#[test] +#[test_log::test] fn test_repeated_save() { let mut file = temp_file!("tests/taglib/data/click.mpc"); diff --git a/lofty/tests/taglib/test_mpeg.rs b/lofty/tests/taglib/test_mpeg.rs index 24e2819b..4c2bd5da 100644 --- a/lofty/tests/taglib/test_mpeg.rs +++ b/lofty/tests/taglib/test_mpeg.rs @@ -12,7 +12,7 @@ use lofty::id3::v2::{Id3v2Tag, Id3v2Version}; use lofty::mpeg::MpegFile; use lofty::tag::Accessor; -#[test] +#[test_log::test] fn test_audio_properties_xing_header_cbr() { let f = get_file::("tests/taglib/data/lame_cbr.mp3"); @@ -25,7 +25,7 @@ fn test_audio_properties_xing_header_cbr() { // CPPUNIT_ASSERT_EQUAL(MPEG::XingHeader::Xing, f.audioProperties()->xingHeader()->type()); } -#[test] +#[test_log::test] fn test_audio_properties_xing_header_vbr() { let f = get_file::("tests/taglib/data/lame_vbr.mp3"); @@ -38,7 +38,7 @@ fn test_audio_properties_xing_header_vbr() { // CPPUNIT_ASSERT_EQUAL(MPEG::XingHeader::Xing, f.audioProperties()->xingHeader()->type()); } -#[test] +#[test_log::test] fn test_audio_properties_vbri_header() { let f = get_file::("tests/taglib/data/rare_frames.mp3"); @@ -51,7 +51,7 @@ fn test_audio_properties_vbri_header() { // CPPUNIT_ASSERT_EQUAL(MPEG::XingHeader::VBRI, f.audioProperties()->xingHeader()->type()); } -#[test] +#[test_log::test] fn test_audio_properties_no_vbr_headers() { let f = get_file::("tests/taglib/data/bladeenc.mp3"); @@ -65,7 +65,7 @@ fn test_audio_properties_no_vbr_headers() { // in Lofty, and it doesn't seem too useful to expose. } -#[test] +#[test_log::test] fn test_skip_invalid_frames_1() { let f = get_file::("tests/taglib/data/invalid-frames1.mp3"); @@ -76,7 +76,7 @@ fn test_skip_invalid_frames_1() { assert_eq!(f.properties().sample_rate(), 44100); } -#[test] +#[test_log::test] fn test_skip_invalid_frames_2() { let f = get_file::("tests/taglib/data/invalid-frames2.mp3"); @@ -87,25 +87,25 @@ fn test_skip_invalid_frames_2() { assert_eq!(f.properties().sample_rate(), 44100); } -#[test] +#[test_log::test] fn test_skip_invalid_frames_3() { let f = get_file::("tests/taglib/data/invalid-frames3.mp3"); assert_eq!(f.properties().duration().as_secs(), 0); assert_eq!(f.properties().duration().as_millis(), 183); // TODO: Off by 26 - assert_eq!(f.properties().audio_bitrate(), 320); + assert_eq!(f.properties().audio_bitrate(), 362); assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().sample_rate(), 44100); } -#[test] +#[test_log::test] fn test_version_2_duration_with_xing_header() { let f = get_file::("tests/taglib/data/mpeg2.mp3"); assert_eq!(f.properties().duration().as_secs(), 5387); // TODO: Off by 15 assert_eq!(f.properties().duration().as_millis(), 5_387_285); } -#[test] +#[test_log::test] fn test_save_id3v24() { let mut file = temp_file!("tests/taglib/data/xing.mp3"); @@ -130,14 +130,14 @@ fn test_save_id3v24() { } } -#[test] +#[test_log::test] #[ignore] fn test_save_id3v24_wrong_param() { // Marker test, Lofty does not replicate the TagLib saving API } // TODO: We don't yet support writing an ID3v23 tag (#62) -#[test] +#[test_log::test] #[ignore] fn test_save_id3v23() { let mut file = temp_file!("tests/taglib/data/xing.mp3"); @@ -163,25 +163,25 @@ fn test_save_id3v23() { } } -#[test] +#[test_log::test] fn test_duplicate_id3v2() { let f = get_file::("tests/taglib/data/duplicate_id3v2.mp3"); assert_eq!(f.properties().sample_rate(), 44100); } -#[test] +#[test_log::test] fn test_fuzzed_file() { let mut file = File::open("tests/taglib/data/excessive_alloc.mp3").unwrap(); assert!(MpegFile::read_from(&mut file, ParseOptions::new()).is_err()) } -#[test] +#[test_log::test] #[ignore] fn test_frame_offset() { // Marker test, Lofty does not replicate this API. Doesn't seem useful to retain frame offsets. } -#[test] +#[test_log::test] fn test_strip_and_properties() { let mut file = temp_file!("tests/taglib/data/xing.mp3"); @@ -213,22 +213,22 @@ fn test_strip_and_properties() { } } -#[test] +#[test_log::test] fn test_properties() {} -#[test] +#[test_log::test] #[ignore] fn test_repeated_save_1() { // Marker test, yet another case of checking frame offsets that Lofty does not expose. } -#[test] +#[test_log::test] #[ignore] fn test_repeated_save_2() { // Marker test, not entirely sure what's even being tested here? } -#[test] +#[test_log::test] fn test_repeated_save_3() { let mut file = temp_file!("tests/taglib/data/xing.mp3"); @@ -270,30 +270,31 @@ fn test_repeated_save_3() { } } -#[test] +#[test_log::test] #[ignore] fn test_empty_id3v2() { // Marker test, Lofty accepts empty strings as valid values } -#[test] +#[test_log::test] #[ignore] fn test_empty_id3v1() { // Marker test, Lofty accepts empty strings as valid values } -#[test] +#[test_log::test] #[ignore] fn test_empty_ape() { // Marker test, Lofty accepts empty strings as valid values } -#[test] +#[test_log::test] fn test_ignore_garbage() { let mut file = temp_file!("tests/taglib/data/garbage.mp3"); { - let mut f = MpegFile::read_from(&mut file, ParseOptions::new().max_junk_bytes(3000)).unwrap(); + let mut f = + MpegFile::read_from(&mut file, ParseOptions::new().max_junk_bytes(3000)).unwrap(); file.rewind().unwrap(); assert!(f.id3v2().is_some()); diff --git a/lofty/tests/taglib/test_ogaflac.rs b/lofty/tests/taglib/test_ogaflac.rs index 6f55d36b..52b23035 100644 --- a/lofty/tests/taglib/test_ogaflac.rs +++ b/lofty/tests/taglib/test_ogaflac.rs @@ -9,7 +9,7 @@ use lofty::ogg::VorbisComments; use lofty::tag::Accessor; // TODO: We don't support FLAC in OGA (#172) -#[test] +#[test_log::test] #[ignore] fn test_framing_bit() { let mut file = temp_file!("tests/taglib/data/empty_flac.oga"); @@ -36,7 +36,7 @@ fn test_framing_bit() { } // TODO: We don't support FLAC in OGA (#172) -#[test] +#[test_log::test] #[ignore] fn test_fuzzed_file() { let mut file = temp_file!("tests/taglib/data/segfault.oga"); @@ -44,7 +44,7 @@ fn test_fuzzed_file() { assert!(f.is_err()); } -#[test] +#[test_log::test] #[ignore] fn test_split_packets() { // Marker test, Lofty does not retain the packet information diff --git a/lofty/tests/taglib/test_ogg.rs b/lofty/tests/taglib/test_ogg.rs index b80e62fe..42daa620 100644 --- a/lofty/tests/taglib/test_ogg.rs +++ b/lofty/tests/taglib/test_ogg.rs @@ -9,7 +9,7 @@ use lofty::file::AudioFile; use lofty::ogg::VorbisFile; use lofty::tag::Accessor; -#[test] +#[test_log::test] fn test_simple() { let mut file = temp_file!("tests/taglib/data/empty.ogg"); @@ -28,13 +28,13 @@ fn test_simple() { } } -#[test] +#[test_log::test] #[ignore] fn test_split_packets1() { // Marker test, Lofty doesn't retain packet information } -#[test] +#[test_log::test] fn test_split_packets2() { let mut file = temp_file!("tests/taglib/data/empty.ogg"); @@ -63,19 +63,19 @@ fn test_split_packets2() { } } -#[test] +#[test_log::test] #[ignore] fn test_dict_interface1() { // Marker test, Lofty doesn't replicate the dictionary interface } -#[test] +#[test_log::test] #[ignore] fn test_dict_interface2() { // Marker test, Lofty doesn't replicate the dictionary interface } -#[test] +#[test_log::test] fn test_audio_properties() { let f = get_file::("tests/taglib/data/empty.ogg"); assert_eq!(f.properties().duration().as_secs(), 3); @@ -90,7 +90,7 @@ fn test_audio_properties() { } // TODO: Need to look into this one, not sure why there's a difference in checksums -#[test] +#[test_log::test] #[ignore] fn test_page_checksum() { let mut file = temp_file!("tests/taglib/data/empty.ogg"); @@ -120,7 +120,7 @@ fn test_page_checksum() { } } -#[test] +#[test_log::test] fn test_page_granule_position() { let mut file = temp_file!("tests/taglib/data/empty.ogg"); diff --git a/lofty/tests/taglib/test_opus.rs b/lofty/tests/taglib/test_opus.rs index 6caac3ec..591845d2 100644 --- a/lofty/tests/taglib/test_opus.rs +++ b/lofty/tests/taglib/test_opus.rs @@ -8,7 +8,7 @@ use lofty::file::AudioFile; use lofty::ogg::OpusFile; use lofty::tag::Accessor; -#[test] +#[test_log::test] fn test_audio_properties() { let f = get_file::("tests/taglib/data/correctness_gain_silent_output.opus"); assert_eq!(f.properties().duration().as_secs(), 7); @@ -19,7 +19,7 @@ fn test_audio_properties() { assert_eq!(f.properties().version(), 1); } -#[test] +#[test_log::test] fn test_read_comments() { let f = get_file::("tests/taglib/data/correctness_gain_silent_output.opus"); assert_eq!( @@ -31,7 +31,7 @@ fn test_read_comments() { assert_eq!(f.vorbis_comments().vendor(), "libopus 0.9.11-66-g64c2dd7"); } -#[test] +#[test_log::test] fn test_write_comments() { let mut file = temp_file!("tests/taglib/data/correctness_gain_silent_output.opus"); @@ -55,7 +55,7 @@ fn test_write_comments() { } } -#[test] +#[test_log::test] #[ignore] fn test_split_packets() { // Marker test, Lofty does not retain packet information diff --git a/lofty/tests/taglib/test_speex.rs b/lofty/tests/taglib/test_speex.rs index aeb6d978..9b58aa4b 100644 --- a/lofty/tests/taglib/test_speex.rs +++ b/lofty/tests/taglib/test_speex.rs @@ -7,7 +7,7 @@ use lofty::tag::Accessor; use std::io::Seek; -#[test] +#[test_log::test] fn test_audio_properties() { let f = get_file::("tests/taglib/data/empty.spx"); @@ -22,7 +22,7 @@ fn test_audio_properties() { } // TODO: This test doesn't work, it's very specific with file/packet sizes. Have to determine whether or not to even keep this one. -#[test] +#[test_log::test] #[ignore] fn test_split_packets() { let mut file = temp_file!("tests/taglib/data/empty.spx"); diff --git a/lofty/tests/taglib/test_synchdata.rs b/lofty/tests/taglib/test_synchdata.rs index fa412b82..fb16a578 100644 --- a/lofty/tests/taglib/test_synchdata.rs +++ b/lofty/tests/taglib/test_synchdata.rs @@ -2,7 +2,7 @@ use std::io::Read; use lofty::id3::v2::util::synchsafe::{SynchsafeInteger, UnsynchronizedStream}; -#[test] +#[test_log::test] fn test1() { let v = u32::from_be_bytes([0, 0, 0, 127]); @@ -10,7 +10,7 @@ fn test1() { assert_eq!(127u32.synch().unwrap(), v); } -#[test] +#[test_log::test] fn test2() { let v = u32::from_be_bytes([0, 0, 1, 0]); @@ -18,7 +18,7 @@ fn test2() { assert_eq!(128u32.synch().unwrap(), v); } -#[test] +#[test_log::test] fn test3() { let v = u32::from_be_bytes([0, 0, 1, 1]); @@ -26,19 +26,19 @@ fn test3() { assert_eq!(129u32.synch().unwrap(), v); } -#[test] +#[test_log::test] #[ignore] fn test_to_uint_broken() { // Marker test, this behavior is not replicated in Lofty } -#[test] +#[test_log::test] #[ignore] fn test_to_uint_broken_and_too_large() { // Marker test, this behavior is not replicated in Lofty } -#[test] +#[test_log::test] fn test_decode1() { let a = [0xFFu8, 0x00u8, 0x00u8]; @@ -51,7 +51,7 @@ fn test_decode1() { assert_eq!(a2, &[0xFF, 0x00]); } -#[test] +#[test_log::test] fn test_decode2() { let a = [0xFFu8, 0x44u8]; @@ -64,7 +64,7 @@ fn test_decode2() { assert_eq!(a2, &[0xFF, 0x44]); } -#[test] +#[test_log::test] fn test_decode3() { let a = [0xFFu8, 0xFFu8, 0x00u8]; @@ -77,7 +77,7 @@ fn test_decode3() { assert_eq!(a2, &[0xFFu8, 0xFFu8]); } -#[test] +#[test_log::test] fn test_decode4() { let a = [0xFFu8, 0xFFu8, 0xFFu8]; diff --git a/lofty/tests/taglib/test_wav.rs b/lofty/tests/taglib/test_wav.rs index 3d782872..8da0d9a5 100644 --- a/lofty/tests/taglib/test_wav.rs +++ b/lofty/tests/taglib/test_wav.rs @@ -8,7 +8,7 @@ use lofty::tag::{Accessor, TagType}; use std::io::{Cursor, Read, Seek, SeekFrom, Write}; -#[test] +#[test_log::test] fn test_pcm_properties() { let f = get_file::("tests/taglib/data/empty.wav"); assert_eq!(f.properties().duration().as_secs(), 3); @@ -21,7 +21,7 @@ fn test_pcm_properties() { assert_eq!(*f.properties().format(), WavFormat::PCM); } -#[test] +#[test_log::test] fn test_alaw_properties() { let f = get_file::("tests/taglib/data/alaw.wav"); assert_eq!(f.properties().duration().as_secs(), 3); @@ -34,7 +34,7 @@ fn test_alaw_properties() { assert_eq!(*f.properties().format(), WavFormat::Other(6)); } -#[test] +#[test_log::test] fn test_float_properties() { let f = get_file::("tests/taglib/data/float64.wav"); assert_eq!(f.properties().duration().as_secs(), 0); @@ -47,7 +47,7 @@ fn test_float_properties() { assert_eq!(*f.properties().format(), WavFormat::IEEE_FLOAT); } -#[test] +#[test_log::test] fn test_float_without_fact_chunk_properties() { let mut wav_data = std::fs::read("tests/taglib/data/float64.wav").unwrap(); assert_eq!(&wav_data[36..40], b"fact"); @@ -66,13 +66,13 @@ fn test_float_without_fact_chunk_properties() { assert_eq!(*f.properties().format(), WavFormat::IEEE_FLOAT); } -#[test] +#[test_log::test] fn test_zero_size_data_chunk() { let mut file = temp_file!("tests/taglib/data/zero-size-chunk.wav"); let _f = WavFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); } -#[test] +#[test_log::test] fn test_id3v2_tag() { let mut file = temp_file!("tests/taglib/data/empty.wav"); @@ -108,7 +108,7 @@ fn test_id3v2_tag() { } } -#[test] +#[test_log::test] fn test_save_id3v23() { let mut file = temp_file!("tests/taglib/data/empty.wav"); @@ -136,7 +136,7 @@ fn test_save_id3v23() { } } -#[test] +#[test_log::test] fn test_info_tag() { let mut file = temp_file!("tests/taglib/data/empty.wav"); @@ -172,7 +172,7 @@ fn test_info_tag() { } } -#[test] +#[test_log::test] fn test_strip_tags() { let mut file = temp_file!("tests/taglib/data/empty.wav"); @@ -229,7 +229,7 @@ fn test_strip_tags() { } } -#[test] +#[test_log::test] #[ignore] fn test_duplicate_tags() { // Marker test, TagLib will ignore any tag except for the first. Lofty will *not* do this. @@ -237,7 +237,7 @@ fn test_duplicate_tags() { // the latest in the stream will have precedence. } -#[test] +#[test_log::test] fn test_fuzzed_file1() { let f1 = get_file::("tests/taglib/data/infloop.wav"); // The file has problems: @@ -252,13 +252,13 @@ fn test_fuzzed_file1() { assert!(f1.id3v2().is_none()); } -#[test] +#[test_log::test] fn test_fuzzed_file2() { let mut file = temp_file!("tests/taglib/data/segfault.wav"); let _f2 = WavFile::read_from(&mut file, ParseOptions::new().read_properties(false)).unwrap(); } -#[test] +#[test_log::test] fn test_file_with_garbage_appended() { let mut file = temp_file!("tests/taglib/data/empty.wav"); let contents_before_modification; @@ -303,13 +303,13 @@ fn test_file_with_garbage_appended() { } } -#[test] +#[test_log::test] #[ignore] fn test_strip_and_properties() { // Marker test, Lofty does not replicate the properties API } -#[test] +#[test_log::test] fn test_pcm_with_fact_chunk() { let f = get_file::("tests/taglib/data/pcm_with_fact_chunk.wav"); assert_eq!(f.properties().duration().as_secs(), 3); @@ -322,7 +322,7 @@ fn test_pcm_with_fact_chunk() { assert_eq!(*f.properties().format(), WavFormat::PCM); } -#[test] +#[test_log::test] fn test_wave_format_extensible() { let f = get_file::("tests/taglib/data/uint8we.wav"); assert_eq!(f.properties().duration().as_secs(), 2); diff --git a/lofty/tests/taglib/test_wavpack.rs b/lofty/tests/taglib/test_wavpack.rs index 692bd0b6..8d784627 100644 --- a/lofty/tests/taglib/test_wavpack.rs +++ b/lofty/tests/taglib/test_wavpack.rs @@ -11,7 +11,7 @@ use lofty::id3::v1::Id3v1Tag; use lofty::tag::Accessor; use lofty::wavpack::WavPackFile; -#[test] +#[test_log::test] fn test_no_length_properties() { let f = get_file::("tests/taglib/data/no_length.wv"); assert_eq!(f.properties().duration().as_secs(), 3); @@ -25,7 +25,7 @@ fn test_no_length_properties() { assert_eq!(f.properties().version(), 1031); } -#[test] +#[test_log::test] #[ignore] fn test_multi_channel_properties() { // Marker test, this is not a valid file and TagLib does not handle it properly. @@ -34,7 +34,7 @@ fn test_multi_channel_properties() { // this file does not. Even FFMpeg thinks this is a mono file. } -#[test] +#[test_log::test] fn test_dsd_stereo_properties() { let f = get_file::("tests/taglib/data/dsd_stereo.wv"); assert_eq!(f.properties().duration().as_secs(), 0); @@ -48,7 +48,7 @@ fn test_dsd_stereo_properties() { assert_eq!(f.properties().version(), 1040); } -#[test] +#[test_log::test] fn test_non_standard_rate_properties() { let f = get_file::("tests/taglib/data/non_standard_rate.wv"); assert_eq!(f.properties().duration().as_secs(), 3); @@ -62,7 +62,7 @@ fn test_non_standard_rate_properties() { assert_eq!(f.properties().version(), 1040); } -#[test] +#[test_log::test] fn test_tagged_properties() { let f = get_file::("tests/taglib/data/tagged.wv"); assert_eq!(f.properties().duration().as_secs(), 3); @@ -76,13 +76,13 @@ fn test_tagged_properties() { assert_eq!(f.properties().version(), 1031); } -#[test] +#[test_log::test] fn test_fuzzed_file() { let mut f = File::open("tests/taglib/data/infloop.wv").unwrap(); assert!(WavPackFile::read_from(&mut f, ParseOptions::new()).is_err()); } -#[test] +#[test_log::test] fn test_strip_and_properties() { let mut file = temp_file!("tests/taglib/data/click.wv"); @@ -113,7 +113,7 @@ fn test_strip_and_properties() { } } -#[test] +#[test_log::test] fn test_repeated_save() { let mut file = temp_file!("tests/taglib/data/click.wv"); diff --git a/lofty/tests/taglib/test_xiphcomment.rs b/lofty/tests/taglib/test_xiphcomment.rs index ccbc7ab8..3aafd129 100644 --- a/lofty/tests/taglib/test_xiphcomment.rs +++ b/lofty/tests/taglib/test_xiphcomment.rs @@ -8,7 +8,7 @@ use lofty::ogg::{OggPictureStorage, VorbisComments, VorbisFile}; use lofty::picture::{MimeType, Picture, PictureInformation, PictureType}; use lofty::tag::{Accessor, TagExt}; -#[test] +#[test_log::test] fn test_year() { let mut cmt = VorbisComments::default(); assert_eq!(cmt.year(), None); @@ -23,7 +23,7 @@ fn test_year() { assert_eq!(cmt.year(), Some(2008)); } -#[test] +#[test_log::test] fn test_set_year() { let mut cmt = VorbisComments::default(); cmt.push(String::from("YEAR"), String::from("2009")); @@ -33,7 +33,7 @@ fn test_set_year() { assert_eq!(cmt.get("DATE"), Some("1995")); } -#[test] +#[test_log::test] fn test_track() { let mut cmt = VorbisComments::default(); assert_eq!(cmt.track(), None); @@ -43,7 +43,7 @@ fn test_track() { assert_eq!(cmt.track(), Some(8)); } -#[test] +#[test_log::test] fn test_set_track() { let mut cmt = VorbisComments::default(); cmt.push(String::from("TRACKNUM"), String::from("7")); @@ -53,13 +53,13 @@ fn test_set_track() { assert_eq!(cmt.get("TRACKNUMBER"), Some("3")); } -#[test] +#[test_log::test] #[ignore] fn test_invalid_keys1() { // Marker test, Lofty does not replicate the properties API } -#[test] +#[test_log::test] fn test_invalid_keys2() { let mut cmt = VorbisComments::default(); cmt.push(String::new(), String::new()); @@ -71,7 +71,7 @@ fn test_invalid_keys2() { assert!(cmt.is_empty()); } -#[test] +#[test_log::test] fn test_clear_comment() { let mut file = temp_file!("tests/taglib/data/empty.ogg"); @@ -91,7 +91,7 @@ fn test_clear_comment() { } } -#[test] +#[test_log::test] #[ignore] fn test_remove_fields() { // Marker test, TagLib has some incredibly strange behavior in this test. @@ -106,7 +106,7 @@ fn test_remove_fields() { // Lofty will never behave in this way. } -#[test] +#[test_log::test] fn test_picture() { let mut file = temp_file!("tests/taglib/data/empty.ogg"); @@ -147,7 +147,7 @@ fn test_picture() { } } -#[test] +#[test_log::test] fn test_lowercase_fields() { let mut file = temp_file!("tests/taglib/data/lowercase-fields.ogg"); From 5ef97b9662e6f943d8b295eb426584596c56677c Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 24 Apr 2025 23:27:08 -0400 Subject: [PATCH 74/75] Tests: Ignore a few TagLib tests for now --- lofty/tests/taglib/test_mp4.rs | 1 + lofty/tests/taglib/test_mpeg.rs | 6 ++++-- lofty/tests/taglib/test_wavpack.rs | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lofty/tests/taglib/test_mp4.rs b/lofty/tests/taglib/test_mp4.rs index ebfcd492..ae7c3521 100644 --- a/lofty/tests/taglib/test_mp4.rs +++ b/lofty/tests/taglib/test_mp4.rs @@ -88,6 +88,7 @@ fn test_properties_alac_without_bitrate() { } #[test_log::test] +#[ignore] // TODO: FFmpeg reports a bitrate of 95kb/s, we report 104 fn test_properties_m4v() { let f = get_file::("tests/taglib/data/blank_video.m4v"); assert_eq!(f.properties().duration().as_secs(), 0); diff --git a/lofty/tests/taglib/test_mpeg.rs b/lofty/tests/taglib/test_mpeg.rs index 4c2bd5da..fdb36d12 100644 --- a/lofty/tests/taglib/test_mpeg.rs +++ b/lofty/tests/taglib/test_mpeg.rs @@ -77,22 +77,24 @@ fn test_skip_invalid_frames_1() { } #[test_log::test] +#[ignore] // TODO: Duration off by 27ms, as reported by FFmpeg fn test_skip_invalid_frames_2() { let f = get_file::("tests/taglib/data/invalid-frames2.mp3"); assert_eq!(f.properties().duration().as_secs(), 0); - assert_eq!(f.properties().duration().as_millis(), 314); // TODO: Off by 79 + assert_eq!(f.properties().duration().as_millis(), 314); assert_eq!(f.properties().audio_bitrate(), 192); assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().sample_rate(), 44100); } #[test_log::test] +#[ignore] // TODO: Duration off by 26ms, as reported by FFmpeg fn test_skip_invalid_frames_3() { let f = get_file::("tests/taglib/data/invalid-frames3.mp3"); assert_eq!(f.properties().duration().as_secs(), 0); - assert_eq!(f.properties().duration().as_millis(), 183); // TODO: Off by 26 + assert_eq!(f.properties().duration().as_millis(), 183); assert_eq!(f.properties().audio_bitrate(), 362); assert_eq!(f.properties().channels(), 2); assert_eq!(f.properties().sample_rate(), 44100); diff --git a/lofty/tests/taglib/test_wavpack.rs b/lofty/tests/taglib/test_wavpack.rs index 8d784627..05662326 100644 --- a/lofty/tests/taglib/test_wavpack.rs +++ b/lofty/tests/taglib/test_wavpack.rs @@ -12,6 +12,7 @@ use lofty::tag::Accessor; use lofty::wavpack::WavPackFile; #[test_log::test] +#[ignore] // TODO: Should we even bother supporting this? FFmpeg also reports zeroed out properties. fn test_no_length_properties() { let f = get_file::("tests/taglib/data/no_length.wv"); assert_eq!(f.properties().duration().as_secs(), 3); From e1acb9e2a45b820151f95e355279e0883e2171db Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 26 Apr 2025 14:43:42 -0400 Subject: [PATCH 75/75] Tests: Fix TagLib TestMP4::testRemoveMetadata --- lofty/tests/taglib/test_mp4.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lofty/tests/taglib/test_mp4.rs b/lofty/tests/taglib/test_mp4.rs index ae7c3521..7df7738b 100644 --- a/lofty/tests/taglib/test_mp4.rs +++ b/lofty/tests/taglib/test_mp4.rs @@ -456,13 +456,16 @@ fn test_remove_metadata() { original_file.read_to_end(&mut original_file_bytes).unwrap(); file.read_to_end(&mut new_file_bytes).unwrap(); - // We need to do some editing, since we preserve the `udta` and `meta` atoms unlike TagLib + // We need to do some editing, since we preserve the `meta` atom unlike TagLib - // Remove the `udta` atom, which should be 53 bytes in length - new_file_bytes.splice(1505..1505 + 53, std::iter::empty()); + // Remove the `udta` atom, which should be 45 bytes in length + new_file_bytes.splice(2785..2785 + 45, std::iter::empty()); // Fix the length of the `moov` atom - new_file_bytes[1500] = 0; + new_file_bytes[1500] = 8; + + // Fix the length of the `udta` atom + new_file_bytes[2780] = 8; assert_eq!(original_file_bytes, new_file_bytes); }