From ebc81eb7d3d2f0dce0876c6a390d12ef63dfd90c Mon Sep 17 00:00:00 2001 From: Alexey Date: Tue, 18 Jul 2023 15:34:05 +0300 Subject: [PATCH] Add tests --- OfficeUtils/tests/.gitignore | 1 + OfficeUtils/tests/main.cpp | 291 +++++++++++++++++++++------- OfficeUtils/tests/tests.pro | 4 +- OfficeUtils/tests/zip/deep_win | Bin 0 -> 1686 bytes OfficeUtils/tests/zip/docx_like_win | Bin 0 -> 6911 bytes OfficeUtils/tests/zip/general_win | Bin 0 -> 5663 bytes 6 files changed, 228 insertions(+), 68 deletions(-) create mode 100644 OfficeUtils/tests/.gitignore create mode 100644 OfficeUtils/tests/zip/deep_win create mode 100644 OfficeUtils/tests/zip/docx_like_win create mode 100644 OfficeUtils/tests/zip/general_win diff --git a/OfficeUtils/tests/.gitignore b/OfficeUtils/tests/.gitignore new file mode 100644 index 0000000000..07fb5b83ca --- /dev/null +++ b/OfficeUtils/tests/.gitignore @@ -0,0 +1 @@ +unzip \ No newline at end of file diff --git a/OfficeUtils/tests/main.cpp b/OfficeUtils/tests/main.cpp index c40570e379..da8cc05223 100644 --- a/OfficeUtils/tests/main.cpp +++ b/OfficeUtils/tests/main.cpp @@ -1,82 +1,241 @@ #include +#include + +#include "gtest/gtest.h" #include "../src/OfficeUtils.h" #include "../../DesktopEditor/common/File.h" #include "../../DesktopEditor/common/Directory.h" -#include "../../DesktopEditor/xml/include/xmlutils.h" -#include "../../DesktopEditor/common/StringBuilder.h" -int main(int argc, char *argv[]) +testing::AssertionResult IsFilesPathsEqual(std::vector files1, std::vector files2) { - std::wstring sep; -#ifdef WIN32 - sep = L"\\"; -#else - sep = L"/"; -#endif // WIN32 + // sort + std::sort(files1.begin(), files1.end()); + std::sort(files2.begin(), files2.end()); - COfficeUtils utils; - std::wstring curr_dir = NSFile::GetProcessDirectory(); - std::wstring output_dir = curr_dir + sep + L"output"; - std::wstring input_dir = curr_dir + sep + L"input"; - std::wstring temp_dir = curr_dir + sep + L"temp"; + // compare count + if(files1.size() != files2.size()) + return testing::AssertionFailure() << "count of files is not equal " + << files1.size() + << " != " + << files2.size(); - if(NSDirectory::Exists(input_dir)) - NSDirectory::DeleteDirectory(input_dir); - NSDirectory::CreateDirectory(input_dir); - - if(NSDirectory::Exists(output_dir)) - NSDirectory::DeleteDirectory(output_dir); - NSDirectory::CreateDirectory(output_dir); - - if(NSDirectory::Exists(temp_dir)) - NSDirectory::DeleteDirectory(temp_dir); - NSDirectory::CreateDirectory(temp_dir); - - std::vector tests; - tests.push_back(L"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"); - tests.push_back(L"-_=+[{]};\',.`~!@#$%^&()!№;%()"); - tests.push_back(L"こんにちは世界 مرحبا بالعالم"); - tests.push_back(L"⌦ⵄ⬺▨⿐⸌⍒⿆₟⹖Ⅼ⌡┓ⓡ⣾⩬⍠⊆≆⳩≾⃉⌮⢸⠐⩏„⨑☰⟬∬⤢⹛⮚∅✢⓴✎ℙ⥅ⅱ‟⥩⩲⺐⫒Ⱌⴳ⒎⹰ⲧ⩷ₗ∸⬐⭹⓾✓⤓≪♽™⠒⌙➴⵶⡣⼷⬖⓭⒱Ȿ⹦⸪⼺⒣≕◼⾙‖"); - -#ifdef LINUX - tests.push_back(L"\\\\hello\\world!\\\\"); -#endif // LINUX - - std::wstring long_test; - for(int i = 0; i < 100; i++) - long_test += L"a"; - tests.push_back(long_test); - - for(int i = 0; i < tests.size(); i++) + // compare filenames + for(size_t i = 0; i < files1.size(); ++i) { - while(!NSDirectory::Exists(input_dir + sep + tests[i])) - NSDirectory::CreateDirectories(input_dir + sep + tests[i]); - std::wstring dir = input_dir + sep + tests[i]; + std::wstring filename1 = NSFile::GetFileName(files1[i]); + std::wstring filename2 = NSFile::GetFileName(files2[i]); - std::wstring file_path = dir + sep + tests[i]; - std::wstring simple_file_path = dir + sep + L"file"; - - NSFile::CFileBinary file; - file.CreateFile(file_path); - file.WriteStringUTF8(file_path, true); - file.CloseFile(); - - file.CreateFile(simple_file_path); - file.WriteStringUTF8(simple_file_path, true); - file.CloseFile(); - - utils.CompressFileOrDirectory(input_dir + sep + tests[i], temp_dir + sep + tests[i] + L".zip"); + if(filename1 != filename2) + return testing::AssertionFailure() << U_TO_UTF8(filename2) + << " is not found"; } - std::vector files = NSDirectory::GetFiles(temp_dir); - - for(auto &file : files) + for(size_t i = 0; i < files1.size(); ++i) { - std::wstring filename = NSFile::GetFileName(file); - NSDirectory::CreateDirectory(output_dir + sep + filename); - utils.ExtractToDirectory(file, output_dir + sep + filename, NULL, false); + if(files1[i] != files2[i]) + return testing::AssertionFailure() << U_TO_UTF8(files1[i]) + << " is not equal " + << U_TO_UTF8(files2[i]); } - - return 0; + return testing::AssertionSuccess(); } + +class COfficeUtilsTest : public testing::Test +{ +public: + COfficeUtils utils; + + std::wstring workDirectory; + std::wstring zipDirectory; + std::wstring unzipDirectory; + std::wstring wsep; + + virtual void SetUp() override + { + +#ifdef WIN32 + wsep = L"\\"; +#else + wsep = L"/"; +#endif // WIN32 + workDirectory = NSFile::GetProcessDirectory(); + unzipDirectory = workDirectory + wsep + L".." + wsep + L"unzip"; + zipDirectory = workDirectory + wsep + L".." + wsep + L"zip"; + + if(!NSDirectory::Exists(unzipDirectory)) + NSDirectory::CreateDirectories(unzipDirectory); + } + + virtual void TearDown() override + { + } +}; + +TEST_F(COfficeUtilsTest, general_win) +{ + std::wstring zip_filename = L"general_win"; + std::wstring zip_path = zipDirectory + wsep + zip_filename; + std::wstring unzip_folder = unzipDirectory + wsep + zip_filename; + + if(NSDirectory::Exists(unzip_folder)) + NSDirectory::DeleteDirectory(unzip_folder); + + NSDirectory::CreateDirectories(unzip_folder); + + HRESULT error_code = utils.ExtractToDirectory(zip_path, unzip_folder, NULL, false); + ASSERT_EQ(error_code, S_OK); + + std::vector unzip_files = NSDirectory::GetFiles(unzip_folder, true); + std::vector actual; + + for(size_t i = 0; i < unzip_files.size(); ++i) + { + size_t subpath_n = unzip_files[i].size() - unzip_folder.size() - 1; + std::wstring subpath = unzip_files[i].substr(unzip_folder.size() + 1, subpath_n); + actual.push_back(subpath); + } + + std::vector expected; + + expected.push_back(L"1.txt"); + expected.push_back(L"2.txt"); + expected.push_back(L"3.txt"); + + expected.push_back(L"こんにちは世界 مرحبا بالعالم" + wsep + L"こんにちは世界 مرحبا بالعالم"); + expected.push_back(L"こんにちは世界 مرحبا بالعالم" + wsep + L"こんにちは世界 مرحبا بالعالم123" + wsep + L"222.txt"); + + expected.push_back(L"НП" + wsep + L"НП" + wsep + L"NF" + wsep + L"new.txt"); + expected.push_back(L"MF" + wsep + L"file.txt"); + + expected.push_back(L"⌦ⵄ⬺▨⿐⸌⍒⿆₟⹖Ⅼ⌡┓ⓡ⣾⩬⍠⊆≆⳩≾⃉⌮⢸⠐⩏„⨑☰⟬∬⤢" + L"⹛⮚∅✢⓴✎ℙ⥅ⅱ‟⥩⩲⺐⫒Ⱌⴳ⒎⹰ⲧ⩷ₗ∸⬐⭹⓾✓⤓≪♽™⠒⌙➴" + L"⵶⡣⼷⬖⓭⒱Ȿ⹦⸪⼺⒣≕◼⾙‖" + wsep + L"⌦ⵄ⬺▨⿐⸌⍒⿆₟⹖Ⅼ⌡┓"); + + expected.push_back(L"-_=+[{]};',.`~!@#$%^&()!№;%()" + wsep + L"-_=+[{]};',.`~!@#$%^&()!№;%()"); + expected.push_back(L"-_=+[{]};',.`~!@#$%^&()!№;%()" + wsep + L"こんにちは世界 مرحبا بالعالم"); + + EXPECT_TRUE(IsFilesPathsEqual(actual, expected)); +} + +TEST_F(COfficeUtilsTest, general_win_no_folder) +{ + std::wstring zip_filename = L"general_win"; + std::wstring zip_path = zipDirectory + wsep + zip_filename; + std::wstring unzip_folder = unzipDirectory + wsep + L"general_win_no_folder"; + + if(NSDirectory::Exists(unzip_folder)) + NSDirectory::DeleteDirectory(unzip_folder); + + NSDirectory::CreateDirectories(unzip_folder); + + HRESULT error_code = utils.ExtractToDirectory(zip_path, unzip_folder, NULL, true); + ASSERT_EQ(error_code, S_OK); + + std::vector unzip_files = NSDirectory::GetFiles(unzip_folder, true); + std::vector actual; + + for(size_t i = 0; i < unzip_files.size(); ++i) + { + size_t subpath_n = unzip_files[i].size() - unzip_folder.size() - 1; + std::wstring subpath = unzip_files[i].substr(unzip_folder.size() + 1, subpath_n); + actual.push_back(subpath); + } + + std::vector expected; + + expected.push_back(L"1.txt"); + expected.push_back(L"2.txt"); + expected.push_back(L"3.txt"); + + expected.push_back(L"こんにちは世界 مرحبا بالعالم"); + expected.push_back(L"222.txt"); + + expected.push_back(L"new.txt"); + expected.push_back(L"file.txt"); + + expected.push_back(L"⌦ⵄ⬺▨⿐⸌⍒⿆₟⹖Ⅼ⌡┓"); + expected.push_back(L"-_=+[{]};',.`~!@#$%^&()!№;%()"); + + EXPECT_TRUE(IsFilesPathsEqual(actual, expected)); +} + +TEST_F(COfficeUtilsTest, deep_win) +{ + std::wstring zip_filename = L"deep_win"; + std::wstring zip_path = zipDirectory + wsep + zip_filename; + std::wstring unzip_folder = unzipDirectory + wsep + zip_filename; + + if(NSDirectory::Exists(unzip_folder)) + NSDirectory::DeleteDirectory(unzip_folder); + + NSDirectory::CreateDirectories(unzip_folder); + + HRESULT error_code = utils.ExtractToDirectory(zip_path, unzip_folder, NULL, false); + ASSERT_EQ(error_code, S_OK); + + std::vector unzip_files = NSDirectory::GetFiles(unzip_folder, true); + std::vector actual; + + for(size_t i = 0; i < unzip_files.size(); ++i) + { + size_t subpath_n = unzip_files[i].size() - unzip_folder.size() - 1; + std::wstring subpath = unzip_files[i].substr(unzip_folder.size() + 1, subpath_n); + actual.push_back(subpath); + } + + std::vector expected; + + expected.push_back(L"1" + wsep + L"2" + wsep + L"3" + wsep + L"4" + wsep + L"hi.txt"); + expected.push_back(L"1" + wsep + L"2" + wsep + L"3" + wsep + L"4" + wsep + L"5" + wsep + + L"6" + wsep + L"7" + wsep + L"8" + wsep + L"9" + wsep + L"10" + wsep + L"hi.txt"); + + EXPECT_TRUE(IsFilesPathsEqual(actual, expected)); +} + +TEST_F(COfficeUtilsTest, docx_like_win) +{ + std::wstring zip_filename = L"docx_like_win"; + std::wstring zip_path = zipDirectory + wsep + zip_filename; + std::wstring unzip_folder = unzipDirectory + wsep + zip_filename; + + if(NSDirectory::Exists(unzip_folder)) + NSDirectory::DeleteDirectory(unzip_folder); + + NSDirectory::CreateDirectories(unzip_folder); + + HRESULT error_code = utils.ExtractToDirectory(zip_path, unzip_folder, NULL, false); + ASSERT_EQ(error_code, S_OK); + + std::vector unzip_files = NSDirectory::GetFiles(unzip_folder, true); + std::vector actual; + + for(size_t i = 0; i < unzip_files.size(); ++i) + { + size_t subpath_n = unzip_files[i].size() - unzip_folder.size() - 1; + std::wstring subpath = unzip_files[i].substr(unzip_folder.size() + 1, subpath_n); + actual.push_back(subpath); + } + + std::vector expected; + + expected.push_back(L"[Content_Types].xml"); + expected.push_back(L"_rels" + wsep + L".rels"); + + expected.push_back(L"docProps" + wsep + L"app.xml"); + expected.push_back(L"docProps" + wsep + L"core.xml"); + + expected.push_back(L"word" + wsep + L"document.xml"); + expected.push_back(L"word" + wsep + L"endnotes.xml"); + expected.push_back(L"word" + wsep + L"fontTable.xml"); + expected.push_back(L"word" + wsep + L"footnotes.xml"); + expected.push_back(L"word" + wsep + L"settings.xml"); + expected.push_back(L"word" + wsep + L"webSettings.xml"); + expected.push_back(L"word" + wsep + L"theme" + wsep + L"theme1.xml"); + + EXPECT_TRUE(IsFilesPathsEqual(actual, expected)); +} + + + + diff --git a/OfficeUtils/tests/tests.pro b/OfficeUtils/tests/tests.pro index 33fa5328e3..9226abd218 100644 --- a/OfficeUtils/tests/tests.pro +++ b/OfficeUtils/tests/tests.pro @@ -12,13 +12,13 @@ $$PWD/../../Common/OfficeFileFormatChecker2.cpp \ SOURCES += main.cpp - CORE_ROOT_DIR = $$PWD/../../ PWD_ROOT_DIR = $$PWD include($$CORE_ROOT_DIR/Common/base.pri) include($$CORE_ROOT_DIR/Common/3dParty/icu/icu.pri) +include($$CORE_ROOT_DIR/Common/3dParty/googletest/googletest.pri) -ADD_DEPENDENCY(kernel, graphics, UnicodeConverter) +ADD_DEPENDENCY(kernel, UnicodeConverter) DESTDIR = $$PWD/build diff --git a/OfficeUtils/tests/zip/deep_win b/OfficeUtils/tests/zip/deep_win new file mode 100644 index 0000000000000000000000000000000000000000..646cb86ea091013e120456e45b0c0c1f197907db GIT binary patch literal 1686 zcmWIWW@h1H00IAkPhnsNlwbnVhWY_;`9L&z7O=b#l8PWS6>Lxy#z<YWOF-P z{zRdhi7c zpLE-wfbMF{T!C!%78JA1P)zrl_&62W^cGPqux-dL$IMB{rr$#|-5kYy*Y>CB$mVw} zp9r=O*?i30hipC}1a}vO@OE3YTl7Y|78< zx%BgU=vB`j&)4fX&dYhd-=Fh2=QBad7?^l~)6d&;Tlegbe}7@2W)BBv3tkXv3mIy0 z1vMA}%59+ovgiQ7EH(f@a=yjF!Q2gM33KIn1hvmeXok)65eJ*FY}2Jn;0vI8j@uPp z*OmIJ>m}6pOg~EO;S{6gb02630Gbot^5KVt=Vb{GoHq0(F0xStV^+KMAl~my} ziYmu@{c3Dt*p%7)9J4pDaU>a;H!#=o?zg1J)!;uK*JH5C#Y*!U zaX89YUCtUpVR2{dJEFPtle#=Ycsqhe;Zw$TNtl7BoILtGu~T&5p0F1S9M`SMNH;ZZ z-l}F6Bl14F>tkb=>x4oF3Z!F$nIn*XFs19hF_ip?va-m&HnrEZSte)hF=3*pF9$hw zb#VjJ>~@<6H}05!ASEQ~15b|#0V}ZZ?x;$FVw!_N1H%h!-hSqn-NIu3AZd~eduu!F zFerK`I&aZLwX%*ur=@Mh+qo!ezg?q|$s1^Xy_1;=1-`z!m7$;6I6MH++Fa6ogDHjN z?^^a%q~TFWoDO0u>E(Q%+0$N8DtF@uGXMRRQ!wrr&M~;%i|#A&okm>4D0a^GF79dA zkf^?wqMnfPN7h2B70KxOXeY7hxgN|beNN!Ss0gb`OQgQxPaQ!t$|DL_M^>mwuPyb5 zmf7G@Uzxerwfu>Um>a$VF>+**VW85h(TA!?MHR^ymR#?Em95#zBcjW1&X{_4xgHGw zaJ%^CEMXQf2Ukm%(>GTYuL_&uBMvHIa&*wT1%E-zWj**p(p`4=x~Y_g5Q$ST!_C4* zZ~1bT4j3`JQ2AU;=l%>$bm!u+i6NWeS^y)LjF7tQD-8`>N8?cO_r5)iVazg3SM8*X zLox9HJ248{?IIgq?Y^@j3N$nS%pav}TVG4XFckwIpL`I>(g`arYz_!X zth?bDe(X$d@vUJeCd@$OB@-PuG8sIl?cxY-MufgywV9!XxgQEQ&C?=2jgc=8Y9XTq zTX@)#xc>|oQ*nig*73Bp5?Qg6@0*^H{P>nP@XBN8ef ze-@r0uEZdUtw*Tma^CCQzKBRKWC~duy;tp~_Z}_GZsbc4- zX7pU+bAnbOdX~kH{(=)$Z=y4r`VYZAY()?YT{2( zT33Bmv69UV$-O3%nhSbWyujL?+f)AH4mRWNTmJY|L9f8%VxJ7gfBK=r!;yqCb(MYX z-ie%oiSq5US&qhr(1-dCgP_%=+YUxoQ(D@E=CZ5yY`70~kOpbQLotJNqL|9oRv;q@ zo2um|x7ECUT2eP{YWn1tDI|+RO)6Q?a@(_N5bu_; zLj$O?`+!C)I>nhI`s5tq&!wz}72N?mFRUHiSJuewisjg32pu%=BUnT442OHjz2LOS z93mt&blu(56;YW(;C+{-&EKM5iIz?F!MLpI8-2BOr&i^KvZ@QJc5uC5R=p^*BIq#r zFd!C7=la34ZN=|A)+jOuk22m6cEafqb(l z^yUi;>Rc*N5a4(~^O9CSDeB6UvXP(#FUb(w&`quFb?NJhg1Y9bx0OmD zpCqnvAfqjfl##u5Pwi(p!4L?4HA7up5l+nY2bs}LH@C+Q84NcP-mCWv;nJ-To41kS zE(_zZP!L2Xe~VsPaID~^$Kc{YgSi;G_&l=v9BT#35}W(hDRs=K>*=dOZM2n^F4z$? z75=!mX^XI$!*`P5@uPybOt8l-#>(#?jB3x{WcMQb%H#)tqB7h5;wf&7Rt6hi4(9SU zauE_v*zQkn+4;~7^}Q9EPT^jcL+TCZ1hT@P73R>kdOHww3(;$JRS*hC7SrS8IJ`bH zD{-OzB=YR%#!YDxY!oiwz)|tbcm1l|V+bDu09Z!_@&BT>i>0fpEzJ6t2h}xGM|sd= zPM!ND+1&A}B(h*W0oHM1Gw~+3;wYUd>j^dG{qOhKoTTujrUwpke%za_^Jv_uvQF#q z+mqs*Mkp$--TGjyxYF=(Xk_aunTx!QM4XeKF2A~AoeFtHVHbfgM{9jD{>>wyj7YgW zMdg5s>~ayv&0rpmmJr^-N#E^ct%|A5z0VcVu~f z*WBOTxWej%sdgQ%X#S4erF->bKup^t%eP6xLj;_&uCfAKV2*Go9Xzep)>nLS;BDsx)&=P^oz6gCO?hUE!^1Vk=iutDR?~ z10kilWSiF>+}$(89gDZ-$()m-6z5pR()hHqf^l_52Otu7B3rWV1VK}m>Q;oLjZ`a|7v6~s5thlDl8sSi z`Z536sV&-Tl^M(?CKNSZ*`apBow^imXO}ZjXfVW$(I`PR1Kw(=dGDXPx1eN&c%9?WZ;MJ=?b($O>C$Q6(3MtYm zRcXI6swDD?txotc!Q7YQmfk6(e7N^*+z%5j(KYm%K~gmHwzVkb)F%0CSD;0k=SOzZ znn!kQniMsFP>Hazf8dmX1#j7xsS@ov&551sRSnWL5E5Sk2=aQ0H=kU&AM;9pAwqQA zB2dvhkTl#f^>%D_(6|9-us-KqlCDTO`gd)Pa=AZvd#%J03oAF=zZq+4!Xq?^K_|rV zEqGgT_S^wO2Xj05M^{V0YHz<&4i^+9(l1TPZ!K49Bf$qb45oQ+=sE8ucyWg4 zQd8(2jFy8WTdm~{5nG-PEj!Pa`#Azr@)hW$Cxf{R>lef1nfR1gNGu4UKYh18rgn}U*7d^ zYW3+U>FB|~@>+cy^Ov~`3Z+u#7fknjJ;L}8$ShBybp6(#7|oSqx2D9L)f)v6p#g$u zpw1`qn{?0Fb9N;gHMfmz#AVfn9diObUq~L(l81Q;WFJ!9w(|!^f3wy|i3-wLkRlKD zZYiD|)nrY0vpV8Q!x2Osv=}2QFbs1^8`=iefCsbPNdf~_rnjnA7K zMFE`xjA&W_DDBr5hz`6W8&GA$S|~;@Y(BnxfK~iH=rQ_W$E^V5LM%Iz-)ihgs~yj* zLJK|I5mK}g{Ik7FuRmTR8Be$>V_IT*^_iZ|wf@ED+N?ylJfQ^@NH2+Ls$>e>V)vqQ zbLfBwa_{~ZjaIP%O?65QJJM|$<@c|TKWvyZqM|0(VNhuAd?DFbQ2_KYU*v#i_d2qLiEOaNM*&e za1LK%2MBDf+O8nn={(r@QfYf(<}JXPe7@b>Ue)$PEG-UMK`g1s1>mQ6uz8UhCw3mW zd^TK6-(Oj_3w*nqHi%7|d$@E$+L#{dn288kOn$OSPixTEVxVEawKi+=6YKIBj2`(d zB%%^FejEUR6m%TZPf;rIi0WnSE_}Sz}prNhu6t zbsTOzMq57t<5DkRf6X++C1PdI{-YU(HASPnzT-%dfBFhc*wEw42^pe_=*s?~bbajM z?Kh@BD2Wx_{jO~w*y3EX1jxVVv$T#Qj&O*77+0P?!|#6WWO2M#O7pDL}@ohz3$P1CY7|wW~Kdn z!Z691BQaM_y7WZ*>uO0%=?UiF6^4sa2CCZxa&~b1U1zLzH6?^7^H@X;;)~nN934+> zBO`WDsg;i`*d%AcM>X%3WxBh9wZ^ry0kzFr%z02gRVjGX+L~&t8|g;<(a(*|MMw0) z;TP~5BD_(3A4Pi={8MQd-IC(GwR$rZ<}rr-Z)WkcS?^ltG}YavFn;zWj(y-hoA(uW z#e!uqU{4;&H-!gCYznX|ogkD`xR_9!ARnWlyBzC-mIODbXO)>*ynT`XNRNYnes71q zcS6ntt^X6pM}uc_lyz%ME_yl+i}>Cu^g~=3yz725Y7&*BokVKEF0p70QJd5WYKq5; z-I*&u*}+?jBwtJbY5Z$Y^G{p|%XJT2L2JD3+O;4P{Cuw^P}?$_5cvSYbkt@%x&u_e zX9f_dk|+_^e)3x>=#P^WPk~a~4r-j12mfi<+`;**=!V7`tG67{d}lfBht&GJ`=M1q$$yBnDX8gjeAo}nxpvu&Ws`V;a6iY z9Uc%#T&dD-pHu{pco2SjrN9*}Ghl}p9;|N>6QzAX0oB?jL$cSH%DlN{=2Gk492(zU z+WfFny$nsX@Y_9m23!_+lf2mq+YOPM3F+}CoV!^PiUeZUN^gQSNshW%udqJ!=3}MI z9$Z1pad-mRJn6q<0$V|Zy`f9{xIVs~PpI^`tHd5Eq0g%*a$tlmpj-b_-Wh^lI^5yn z(z0p!QEgZY8UGLhl7%Vz28+r&^d3vsNIlW*Pc6?(vKS&kr($sYNcQ)ZMRPruW*c>0}%96SKS*#Zr44Ip(_g46l!1lsxBlaeoNi(3DJ{aefR z9QG9F25OwaUTnZ#qyQ*{)(0==qs=b=g8m!D_9`TYAiE|=05IVMnX z`HbUYVf`lxzmPvs{H^9Vr#O8_q^MwUM)9ixxd=XY^b2%M&T_{I^@`5dzsrBQPB{lZ zeNCd5!T+~vxkz*VqR!#Z=ig~ACtMe4CV~H+;kWebPb^)(DV5h_@9Nwy(DxZs@eCSgTDxU4s|Z~-;{ni&|Z|g;Ue_EVfP}# u@529$;c`5>$nXy;@%>Nu^XT+{QJfzNq>Oc%YypT+KW|XZK0Q@7*l-p7WjWoO|w< z*jgznsUw7#=%UvU>sEl1#Zm@B3uYrU3x8^C;2Ypub81^`%Lb;!p9#lQbXqSl=ZAb9 zJ(}eOx;AcYp58@I^S!+WJ5*GIS`2oLDc0R_OwqIT*v_51ZN$1UJUhjrTIDRda4L0P zVP0UIRj{{jOZ^AzVZNqkgTw8HPYx|BU|i{b6m;D=DRSg$=fk$qLQ7wVUcdHLi#P)g z@2%WezOVMz+pPA=e1&;){hrx7GQ84sf^}3IA4i2H|NJF?Wml`8c96eX<}*9KgUtzp zTD@VN`|9!Y1NZbN&b8pCc5JU`_b+|$veMJhN&A(1RLY~5);HEQjvh>C*VejW$nnb1 zKJCpcJ*w@xwb?yW-O|=5@NS>p!(fe)ZujrherUDQwZF=ayJggREYi~Tv+s7VV+EPL z$W%6+HO8s z>nfeq`*hc=)gIftdd*rxKcCfz@fXLWPu4UDPZUfPr^g*T3CI{I)5cCT@wFL!I*k)q zwgnLUe0Cs4uh%^nS+ghv(|q3b`DOZ0Bps`aH(2^O}9oP2($jcW+;e zL=iBV=n#&e{E%j{Ax2NGUHF>Dx?z7Pw?B;AA6nWUn$sV8_I`chz-hKtPXxb;U(PS) z7ip1OPdJ}P?mZEKeH&kV(|Nxwrc~41AgHhY@5`~C9YxRA^m$*XdUC0E`T8UJ4rr0N z(~&`?1al`r@#7RS5mLj&kKv3zp5Rx&E`*wJC~JVkVrY(sdN#yxpg9u4vVfNe5oN&6 zfm2-I=0IK(l$1eCHk^!v=t!t8f#@bU5e@7L$gPL$cqoa3kW45}faFTZDubvp$j=2{ z8dRi0R0O2w0{1+m$3l1t6huHo6@+9#K?#(cgNAr0EbuDfToIJiL0A$* z)k9f4l=Fbw1nFGJ=R))uNVx#vxsc5Pb_!&khniZ*$%Do^C`$xxIdH0=stI_7P=5v* z8-SAs(WfD)5t>pUB+*cuswJ2X%{{zl`j`eid}-ir`qt4g!+;rji2rJaa|y%3o)Huv zS#XM&WgAN>1-K+v;1t}f8BxY(7*fI49N~n6p56yYQ#LU*W^FJtRub$lp-CiVaz2WQ z!@WioF;+Y{$hekqElc1)I9*}FbY--dOc$(JfEXbQ^mPR*CM>g?uv2EEC8A|JEwL{( z#E5G9sZYETe=2Tb_IU9vA3aAB6uB0n_r;5k95_xQ^YdAT7$vKoH%ZtKeQBk?laULp z>QvZx>kj%zGPwcUm9uS72q$S$F0WxtPVcYXwEmb7x!aq^YGiCeBWnl)cp2HvAtH+u zu-AVBz}Q%z=uFJal&RwdZqT*zo8HY=SHy4g+mP!R}By4fo37r*b5X;#>|r1h4K2<1XN z`YOnl6+-nz_jhc|Z~nwz>~Zg3eDG+;5{4m%LKDX;t? z1(1}3xzbxGUFZ}pd%C}xP{^HqrB!zhY;~Y!tJ&Hn7xR$uT&rsm}f5h+%1Ew>r zqxNInl*NjBhHQ zWH%GU&kKzFi4)U%HZVkAc#k?aV#0>Mu)pkxDsR3NEw|dL`mNix{#Kl z<^l&o7wfS66$(?j7iIP1Zo~^UioHxCUd67Y*DCS`UQ|Jag~HF^JfA)rOVoQ{1F zdNiM4H9aXLJj2a&js*5el{+2#2*xhZ@pQ8jWg;cpY^`SDhJ^pm!Ot$d15C-!X7mr7 CS#t&e literal 0 HcmV?d00001