From 24d1a9b2200d96e814cc5cb15c89f4dbcdd1645a Mon Sep 17 00:00:00 2001 From: Alexey Date: Tue, 18 Jul 2023 16:00:42 +0300 Subject: [PATCH] Add more tests --- OfficeUtils/tests/main.cpp | 200 ++++++++++++++++++++------ OfficeUtils/tests/zip/deep_linux | Bin 0 -> 2054 bytes OfficeUtils/tests/zip/docx_like_linux | Bin 0 -> 7511 bytes OfficeUtils/tests/zip/general_linux | Bin 0 -> 6032 bytes 4 files changed, 157 insertions(+), 43 deletions(-) create mode 100644 OfficeUtils/tests/zip/deep_linux create mode 100644 OfficeUtils/tests/zip/docx_like_linux create mode 100644 OfficeUtils/tests/zip/general_linux diff --git a/OfficeUtils/tests/main.cpp b/OfficeUtils/tests/main.cpp index da8cc05223..f0097effff 100644 --- a/OfficeUtils/tests/main.cpp +++ b/OfficeUtils/tests/main.cpp @@ -51,6 +51,11 @@ public: std::wstring unzipDirectory; std::wstring wsep; + std::vector expected_general; + std::vector expected_general_no_folder; + std::vector expected_deep; + std::vector expected_docx_like; + virtual void SetUp() override { @@ -65,6 +70,59 @@ public: if(!NSDirectory::Exists(unzipDirectory)) NSDirectory::CreateDirectories(unzipDirectory); + + // general + expected_general.push_back(L"1.txt"); + expected_general.push_back(L"2.txt"); + expected_general.push_back(L"3.txt"); + + expected_general.push_back(L"こんにちは世界 مرحبا بالعالم" + wsep + L"こんにちは世界 مرحبا بالعالم"); + expected_general.push_back(L"こんにちは世界 مرحبا بالعالم" + wsep + L"こんにちは世界 مرحبا بالعالم123" + wsep + L"222.txt"); + + expected_general.push_back(L"НП" + wsep + L"НП" + wsep + L"NF" + wsep + L"new.txt"); + expected_general.push_back(L"MF" + wsep + L"file.txt"); + + expected_general.push_back(L"⌦ⵄ⬺▨⿐⸌⍒⿆₟⹖Ⅼ⌡┓ⓡ⣾⩬⍠⊆≆⳩≾⃉⌮⢸⠐⩏„⨑☰⟬∬⤢" + L"⹛⮚∅✢⓴✎ℙ⥅ⅱ‟⥩⩲⺐⫒Ⱌⴳ⒎⹰ⲧ⩷ₗ∸⬐⭹⓾✓⤓≪♽™⠒⌙➴" + L"⵶⡣⼷⬖⓭⒱Ȿ⹦⸪⼺⒣≕◼⾙‖" + wsep + L"⌦ⵄ⬺▨⿐⸌⍒⿆₟⹖Ⅼ⌡┓"); + + expected_general.push_back(L"-_=+[{]};',.`~!@#$%^&()!№;%()" + wsep + L"-_=+[{]};',.`~!@#$%^&()!№;%()"); + expected_general.push_back(L"-_=+[{]};',.`~!@#$%^&()!№;%()" + wsep + L"こんにちは世界 مرحبا بالعالم"); + + // general_no_folder + expected_general_no_folder.push_back(L"1.txt"); + expected_general_no_folder.push_back(L"2.txt"); + expected_general_no_folder.push_back(L"3.txt"); + + expected_general_no_folder.push_back(L"こんにちは世界 مرحبا بالعالم"); + expected_general_no_folder.push_back(L"222.txt"); + + expected_general_no_folder.push_back(L"new.txt"); + expected_general_no_folder.push_back(L"file.txt"); + + expected_general_no_folder.push_back(L"⌦ⵄ⬺▨⿐⸌⍒⿆₟⹖Ⅼ⌡┓"); + expected_general_no_folder.push_back(L"-_=+[{]};',.`~!@#$%^&()!№;%()"); + + // deep + expected_deep.push_back(L"1" + wsep + L"2" + wsep + L"3" + wsep + L"4" + wsep + L"hi.txt"); + expected_deep.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"); + + // docx_like + expected_docx_like.push_back(L"[Content_Types].xml"); + expected_docx_like.push_back(L"_rels" + wsep + L".rels"); + + expected_docx_like.push_back(L"docProps" + wsep + L"app.xml"); + expected_docx_like.push_back(L"docProps" + wsep + L"core.xml"); + + expected_docx_like.push_back(L"word" + wsep + L"document.xml"); + expected_docx_like.push_back(L"word" + wsep + L"endnotes.xml"); + expected_docx_like.push_back(L"word" + wsep + L"fontTable.xml"); + expected_docx_like.push_back(L"word" + wsep + L"footnotes.xml"); + expected_docx_like.push_back(L"word" + wsep + L"settings.xml"); + expected_docx_like.push_back(L"word" + wsep + L"webSettings.xml"); + expected_docx_like.push_back(L"word" + wsep + L"theme" + wsep + L"theme1.xml"); + } virtual void TearDown() override @@ -96,26 +154,44 @@ TEST_F(COfficeUtilsTest, general_win) actual.push_back(subpath); } - std::vector expected; + EXPECT_TRUE(IsFilesPathsEqual(actual, expected_general)); - expected.push_back(L"1.txt"); - expected.push_back(L"2.txt"); - expected.push_back(L"3.txt"); + // data check + std::wstring data; + ASSERT_TRUE(NSFile::CFileBinary::ReadAllTextUtf8(unzip_folder + wsep + L"MF" + wsep + L"file.txt", data)); + EXPECT_EQ(data, L"123 321"); +} - expected.push_back(L"こんにちは世界 مرحبا بالعالم" + wsep + L"こんにちは世界 مرحبا بالعالم"); - expected.push_back(L"こんにちは世界 مرحبا بالعالم" + wsep + L"こんにちは世界 مرحبا بالعالم123" + wsep + L"222.txt"); +TEST_F(COfficeUtilsTest, general_linux) +{ + std::wstring zip_filename = L"general_linux"; + std::wstring zip_path = zipDirectory + wsep + zip_filename; + std::wstring unzip_folder = unzipDirectory + wsep + zip_filename; - expected.push_back(L"НП" + wsep + L"НП" + wsep + L"NF" + wsep + L"new.txt"); - expected.push_back(L"MF" + wsep + L"file.txt"); + if(NSDirectory::Exists(unzip_folder)) + NSDirectory::DeleteDirectory(unzip_folder); - expected.push_back(L"⌦ⵄ⬺▨⿐⸌⍒⿆₟⹖Ⅼ⌡┓ⓡ⣾⩬⍠⊆≆⳩≾⃉⌮⢸⠐⩏„⨑☰⟬∬⤢" - L"⹛⮚∅✢⓴✎ℙ⥅ⅱ‟⥩⩲⺐⫒Ⱌⴳ⒎⹰ⲧ⩷ₗ∸⬐⭹⓾✓⤓≪♽™⠒⌙➴" - L"⵶⡣⼷⬖⓭⒱Ȿ⹦⸪⼺⒣≕◼⾙‖" + wsep + L"⌦ⵄ⬺▨⿐⸌⍒⿆₟⹖Ⅼ⌡┓"); + NSDirectory::CreateDirectories(unzip_folder); - expected.push_back(L"-_=+[{]};',.`~!@#$%^&()!№;%()" + wsep + L"-_=+[{]};',.`~!@#$%^&()!№;%()"); - expected.push_back(L"-_=+[{]};',.`~!@#$%^&()!№;%()" + wsep + L"こんにちは世界 مرحبا بالعالم"); + HRESULT error_code = utils.ExtractToDirectory(zip_path, unzip_folder, NULL, false); + ASSERT_EQ(error_code, S_OK); - EXPECT_TRUE(IsFilesPathsEqual(actual, expected)); + 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); + } + + EXPECT_TRUE(IsFilesPathsEqual(actual, expected_general)); + + // data check + std::wstring data; + ASSERT_TRUE(NSFile::CFileBinary::ReadAllTextUtf8(unzip_folder + wsep + L"MF" + wsep + L"file.txt", data)); + EXPECT_EQ(data, L"123 321"); } TEST_F(COfficeUtilsTest, general_win_no_folder) @@ -141,23 +217,33 @@ TEST_F(COfficeUtilsTest, general_win_no_folder) std::wstring subpath = unzip_files[i].substr(unzip_folder.size() + 1, subpath_n); actual.push_back(subpath); } + EXPECT_TRUE(IsFilesPathsEqual(actual, expected_general_no_folder)); +} - std::vector expected; +TEST_F(COfficeUtilsTest, general_linux_no_folder) +{ + std::wstring zip_filename = L"general_linux"; + std::wstring zip_path = zipDirectory + wsep + zip_filename; + std::wstring unzip_folder = unzipDirectory + wsep + L"general_linux_no_folder"; - expected.push_back(L"1.txt"); - expected.push_back(L"2.txt"); - expected.push_back(L"3.txt"); + if(NSDirectory::Exists(unzip_folder)) + NSDirectory::DeleteDirectory(unzip_folder); - expected.push_back(L"こんにちは世界 مرحبا بالعالم"); - expected.push_back(L"222.txt"); + NSDirectory::CreateDirectories(unzip_folder); - expected.push_back(L"new.txt"); - expected.push_back(L"file.txt"); + HRESULT error_code = utils.ExtractToDirectory(zip_path, unzip_folder, NULL, true); + ASSERT_EQ(error_code, S_OK); - expected.push_back(L"⌦ⵄ⬺▨⿐⸌⍒⿆₟⹖Ⅼ⌡┓"); - expected.push_back(L"-_=+[{]};',.`~!@#$%^&()!№;%()"); + std::vector unzip_files = NSDirectory::GetFiles(unzip_folder, true); + std::vector actual; - EXPECT_TRUE(IsFilesPathsEqual(actual, expected)); + 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); + } + EXPECT_TRUE(IsFilesPathsEqual(actual, expected_general_no_folder)); } TEST_F(COfficeUtilsTest, deep_win) @@ -183,14 +269,33 @@ TEST_F(COfficeUtilsTest, deep_win) std::wstring subpath = unzip_files[i].substr(unzip_folder.size() + 1, subpath_n); actual.push_back(subpath); } + EXPECT_TRUE(IsFilesPathsEqual(actual, expected_deep)); +} - std::vector expected; +TEST_F(COfficeUtilsTest, deep_linux) +{ + std::wstring zip_filename = L"deep_linux"; + std::wstring zip_path = zipDirectory + wsep + zip_filename; + std::wstring unzip_folder = unzipDirectory + wsep + zip_filename; - 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"); + if(NSDirectory::Exists(unzip_folder)) + NSDirectory::DeleteDirectory(unzip_folder); - EXPECT_TRUE(IsFilesPathsEqual(actual, expected)); + 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); + } + EXPECT_TRUE(IsFilesPathsEqual(actual, expected_deep)); } TEST_F(COfficeUtilsTest, docx_like_win) @@ -216,24 +321,33 @@ TEST_F(COfficeUtilsTest, docx_like_win) std::wstring subpath = unzip_files[i].substr(unzip_folder.size() + 1, subpath_n); actual.push_back(subpath); } + EXPECT_TRUE(IsFilesPathsEqual(actual, expected_docx_like)); +} - std::vector expected; +TEST_F(COfficeUtilsTest, docx_like_linux) +{ + std::wstring zip_filename = L"docx_like_linux"; + std::wstring zip_path = zipDirectory + wsep + zip_filename; + std::wstring unzip_folder = unzipDirectory + wsep + zip_filename; - expected.push_back(L"[Content_Types].xml"); - expected.push_back(L"_rels" + wsep + L".rels"); + if(NSDirectory::Exists(unzip_folder)) + NSDirectory::DeleteDirectory(unzip_folder); - expected.push_back(L"docProps" + wsep + L"app.xml"); - expected.push_back(L"docProps" + wsep + L"core.xml"); + NSDirectory::CreateDirectories(unzip_folder); - 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"); + HRESULT error_code = utils.ExtractToDirectory(zip_path, unzip_folder, NULL, false); + ASSERT_EQ(error_code, S_OK); - EXPECT_TRUE(IsFilesPathsEqual(actual, expected)); + 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); + } + EXPECT_TRUE(IsFilesPathsEqual(actual, expected_docx_like)); } diff --git a/OfficeUtils/tests/zip/deep_linux b/OfficeUtils/tests/zip/deep_linux new file mode 100644 index 0000000000000000000000000000000000000000..3fbfbc5181c7af46025a3d70d74920f7eb9b2625 GIT binary patch literal 2054 zcmbu=&r1S96bJCcYbm-JwXlBRttCQ zfHKtyP_`_r2DdMuO?3veOMh%mpFo}J6sSKFI;U@-PjwFTze%6fNAwodRaBiM k812@oeDmtiq=YVhPUMB$f_pE=)N2Uh@>8$@UL*(b3-EuDPyhe` literal 0 HcmV?d00001 diff --git a/OfficeUtils/tests/zip/docx_like_linux b/OfficeUtils/tests/zip/docx_like_linux new file mode 100644 index 0000000000000000000000000000000000000000..1ac0b0e8877f4505cf02c96ad86d19abf007b3ae GIT binary patch literal 7511 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