// Copyright (c) 2014-2017 Thomas Fussell // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE // // @license: http://www.opensource.org/licenses/mit-license.php // @author: see AUTHORS file #pragma once #include #include #include #include #include #include #include #include #include #include class serialization_test_suite : public test_suite { public: serialization_test_suite() { register_test(test_produce_empty); register_test(test_produce_simple_excel); register_test(test_save_after_sheet_deletion); register_test(test_write_comments_hyperlinks_formulae); register_test(test_save_after_clear_all_formulae); register_test(test_load_non_xlsx); register_test(test_decrypt_agile); register_test(test_decrypt_libre_office); register_test(test_decrypt_standard); register_test(test_decrypt_numbers); register_test(test_read_unicode_filename); register_test(test_comments); register_test(test_read_hyperlink); register_test(test_read_formulae); register_test(test_read_headers_and_footers); register_test(test_read_custom_properties); register_test(test_round_trip_rw); register_test(test_round_trip_rw_encrypted); register_test(test_streaming_read); register_test(test_streaming_write); } bool workbook_matches_file(xlnt::workbook &wb, const xlnt::path &file) { std::vector wb_data; wb.save(wb_data); std::ifstream file_stream(file.string(), std::ios::binary); auto file_data = xlnt::detail::to_vector(file_stream); return xml_helper::xlsx_archives_match(wb_data, file_data); } void test_produce_empty() { xlnt::workbook wb; const auto path = path_helper::test_file("3_default.xlsx"); xlnt_assert(workbook_matches_file(wb, path)); } void test_produce_simple_excel() { xlnt::workbook wb; auto ws = wb.active_sheet(); auto bold_font = xlnt::font().bold(true); ws.cell("A1").value("Type"); ws.cell("A1").font(bold_font); ws.cell("B1").value("Value"); ws.cell("B1").font(bold_font); ws.cell("A2").value("null"); ws.cell("B2").value(nullptr); ws.cell("A3").value("bool (true)"); ws.cell("B3").value(true); ws.cell("A4").value("bool (false)"); ws.cell("B4").value(false); ws.cell("A5").value("number (int)"); ws.cell("B5").value(std::numeric_limits::max()); ws.cell("A5").value("number (unsigned int)"); ws.cell("B5").value(std::numeric_limits::max()); ws.cell("A6").value("number (long long int)"); ws.cell("B6").value(std::numeric_limits::max()); ws.cell("A6").value("number (unsigned long long int)"); ws.cell("B6").value(std::numeric_limits::max()); ws.cell("A13").value("number (float)"); ws.cell("B13").value(std::numeric_limits::max()); ws.cell("A14").value("number (double)"); ws.cell("B14").value(std::numeric_limits::max()); ws.cell("A15").value("number (long double)"); ws.cell("B15").value(std::numeric_limits::max()); ws.cell("A16").value("text (char *)"); ws.cell("B16").value("string"); ws.cell("A17").value("text (std::string)"); ws.cell("B17").value(std::string("string")); ws.cell("A18").value("date"); ws.cell("B18").value(xlnt::date(2016, 2, 3)); ws.cell("A19").value("time"); ws.cell("B19").value(xlnt::time(1, 2, 3, 4)); ws.cell("A20").value("datetime"); ws.cell("B20").value(xlnt::datetime(2016, 2, 3, 1, 2, 3, 4)); ws.cell("A21").value("timedelta"); ws.cell("B21").value(xlnt::timedelta(1, 2, 3, 4, 5)); ws.freeze_panes("B2"); std::vector temp_buffer; wb.save(temp_buffer); xlnt_assert(!temp_buffer.empty()); } void test_save_after_sheet_deletion() { xlnt::workbook workbook; xlnt_assert_equals(workbook.sheet_titles().size(), 1); auto sheet = workbook.create_sheet(); sheet.title("XXX1"); xlnt_assert_equals(workbook.sheet_titles().size(), 2); workbook.remove_sheet(workbook.sheet_by_title("XXX1")); xlnt_assert_equals(workbook.sheet_titles().size(), 1); std::vector temp_buffer; xlnt_assert_throws_nothing(workbook.save(temp_buffer)); xlnt_assert(!temp_buffer.empty()); } void test_write_comments_hyperlinks_formulae() { xlnt::workbook wb; auto sheet1 = wb.active_sheet(); auto comment_font = xlnt::font().bold(true).size(10).color(xlnt::indexed_color(81)).name("Calibri"); sheet1.cell("A1").value("Sheet1!A1"); sheet1.cell("A1").comment("Sheet1 comment", comment_font, "Microsoft Office User"); sheet1.cell("A2").value("Sheet1!A2"); sheet1.cell("A2").comment("Sheet1 comment2", comment_font, "Microsoft Office User"); sheet1.cell("A4").hyperlink("https://microsoft.com", "hyperlink1"); sheet1.cell("A5").hyperlink("https://google.com"); sheet1.cell("A6").hyperlink(sheet1.cell("A1")); sheet1.cell("A7").hyperlink("mailto:invalid@example.com?subject=important"); sheet1.cell("C1").formula("=CONCATENATE(C2,C3)"); sheet1.cell("C2").value("a"); sheet1.cell("C3").value("b"); auto sheet2 = wb.create_sheet(); sheet2.cell("A1").value("Sheet2!A1"); sheet2.cell("A2").comment("Sheet2 comment", comment_font, "Microsoft Office User"); sheet2.cell("A2").value("Sheet2!A2"); sheet2.cell("A2").comment("Sheet2 comment2", comment_font, "Microsoft Office User"); sheet2.cell("A4").hyperlink("https://apple.com", "hyperlink2"); sheet2.cell("C1").formula("=C2*C3"); sheet2.cell("C2").value(2); sheet2.cell("C3").value(3); const auto path = path_helper::test_file("10_comments_hyperlinks_formulae.xlsx"); xlnt_assert(workbook_matches_file(wb, path)); } void test_save_after_clear_all_formulae() { xlnt::workbook wb; const auto path = path_helper::test_file("10_comments_hyperlinks_formulae.xlsx"); wb.load(path); auto ws1 = wb.sheet_by_index(0); xlnt_assert(ws1.cell("C1").has_formula()); xlnt_assert_equals(ws1.cell("C1").formula(), "CONCATENATE(C2,C3)"); ws1.cell("C1").clear_formula(); auto ws2 = wb.sheet_by_index(1); xlnt_assert(ws2.cell("C1").has_formula()); xlnt_assert_equals(ws2.cell("C1").formula(), "C2*C3"); ws2.cell("C1").clear_formula(); wb.save("clear_formulae.xlsx"); } void test_load_non_xlsx() { xlnt::workbook wb; const auto path = path_helper::test_file("1_powerpoint_presentation.xlsx"); xlnt_assert_throws(wb.load(path), xlnt::invalid_file); } void test_decrypt_agile() { xlnt::workbook wb; const auto path = path_helper::test_file("5_encrypted_agile.xlsx"); xlnt_assert_throws(wb.load(path, "incorrect"), xlnt::exception); xlnt_assert_throws_nothing(wb.load(path, "secret")); } void test_decrypt_libre_office() { xlnt::workbook wb; const auto path = path_helper::test_file("6_encrypted_libre.xlsx"); xlnt_assert_throws(wb.load(path, "incorrect"), xlnt::exception); xlnt_assert_throws_nothing(wb.load(path, u8"пароль")); } void test_decrypt_standard() { xlnt::workbook wb; const auto path = path_helper::test_file("7_encrypted_standard.xlsx"); xlnt_assert_throws(wb.load(path, "incorrect"), xlnt::exception); xlnt_assert_throws_nothing(wb.load(path, "password")); } void test_decrypt_numbers() { xlnt::workbook wb; const auto path = path_helper::test_file("8_encrypted_numbers.xlsx"); xlnt_assert_throws(wb.load(path, "incorrect"), xlnt::exception); xlnt_assert_throws_nothing(wb.load(path, "secret")); } void test_read_unicode_filename() { #ifdef _MSC_VER xlnt::workbook wb; const auto path = LSTRING_LITERAL(XLNT_TEST_DATA_DIR) L"/9_unicode_Λ.xlsx"; wb.load(path); xlnt_assert_equals(wb.active_sheet().cell("A1").value(), u8"unicodê!"); #endif #ifndef __MINGW32__ xlnt::workbook wb2; const auto path2 = U8STRING_LITERAL(XLNT_TEST_DATA_DIR) u8"/9_unicode_Λ.xlsx"; wb2.load(path2); xlnt_assert_equals(wb2.active_sheet().cell("A1").value(), u8"unicodê!"); #endif } void test_comments() { xlnt::workbook wb; const auto path = path_helper::test_file("10_comments_hyperlinks_formulae.xlsx"); wb.load(path); auto sheet1 = wb[0]; xlnt_assert_equals(sheet1.cell("A1").value(), "Sheet1!A1"); xlnt_assert_equals(sheet1.cell("A1").comment().plain_text(), "Sheet1 comment"); xlnt_assert_equals(sheet1.cell("A1").comment().author(), "Microsoft Office User"); auto sheet2 = wb[1]; xlnt_assert_equals(sheet2.cell("A1").value(), "Sheet2!A1"); xlnt_assert_equals(sheet2.cell("A1").comment().plain_text(), "Sheet2 comment"); xlnt_assert_equals(sheet2.cell("A1").comment().author(), "Microsoft Office User"); } void test_read_hyperlink() { xlnt::workbook wb; const auto path = path_helper::test_file("10_comments_hyperlinks_formulae.xlsx"); wb.load(path); auto ws1 = wb.sheet_by_index(0); xlnt_assert_equals(ws1.title(), "Sheet1"); xlnt_assert(ws1.cell("A4").has_hyperlink()); xlnt_assert_equals(ws1.cell("A4").value(), "hyperlink1"); xlnt_assert_equals(ws1.cell("A4").hyperlink(), "https://microsoft.com/"); xlnt_assert(ws1.cell("A5").has_hyperlink()); xlnt_assert_equals(ws1.cell("A5").value(), "https://google.com/"); xlnt_assert_equals(ws1.cell("A5").hyperlink(), "https://google.com/"); //xlnt_assert(ws1.cell("A6").has_hyperlink()); xlnt_assert_equals(ws1.cell("A6").value(), "Sheet1!A1"); //xlnt_assert_equals(ws1.cell("A6").hyperlink(), "Sheet1!A1"); xlnt_assert(ws1.cell("A7").has_hyperlink()); xlnt_assert_equals(ws1.cell("A7").value(), "mailto:invalid@example.com?subject=important"); xlnt_assert_equals(ws1.cell("A7").hyperlink(), "mailto:invalid@example.com?subject=important"); } void test_read_formulae() { xlnt::workbook wb; const auto path = path_helper::test_file("10_comments_hyperlinks_formulae.xlsx"); wb.load(path); auto ws1 = wb.sheet_by_index(0); xlnt_assert_equals(ws1.cell("C1").value(), "ab"); xlnt_assert(ws1.cell("C1").has_formula()); xlnt_assert_equals(ws1.cell("C1").formula(), "CONCATENATE(C2,C3)"); xlnt_assert_equals(ws1.cell("C2").value(), "a"); xlnt_assert_equals(ws1.cell("C3").value(), "b"); auto ws2 = wb.sheet_by_index(1); xlnt_assert_equals(ws2.cell("C1").value(), 6); xlnt_assert(ws2.cell("C1").has_formula()); xlnt_assert_equals(ws2.cell("C1").formula(), "C2*C3"); xlnt_assert_equals(ws2.cell("C2").value(), 2); xlnt_assert_equals(ws2.cell("C3").value(), 3); } void test_read_headers_and_footers() { xlnt::workbook wb; wb.load(path_helper::test_file("11_print_settings.xlsx")); auto ws = wb.active_sheet(); xlnt_assert_equals(ws.cell("A1").value(), "header"); xlnt_assert_equals(ws.cell("A2").value(), "and"); xlnt_assert_equals(ws.cell("A3").value(), "footer"); xlnt_assert_equals(ws.cell("A4").value(), "page1"); xlnt_assert_equals(ws.cell("A43").value(), "page2"); xlnt_assert(ws.has_header_footer()); xlnt_assert(ws.header_footer().align_with_margins()); xlnt_assert(ws.header_footer().scale_with_doc()); xlnt_assert(!ws.header_footer().different_first()); xlnt_assert(!ws.header_footer().different_odd_even()); xlnt_assert(ws.header_footer().has_header(xlnt::header_footer::location::left)); xlnt_assert_equals(ws.header_footer().header(xlnt::header_footer::location::left).plain_text(), "left header"); xlnt_assert(ws.header_footer().has_header(xlnt::header_footer::location::center)); xlnt_assert_equals(ws.header_footer().header(xlnt::header_footer::location::center).plain_text(), "center header"); xlnt_assert(ws.header_footer().has_header(xlnt::header_footer::location::right)); xlnt_assert_equals(ws.header_footer().header(xlnt::header_footer::location::right).plain_text(), "right header"); xlnt_assert(ws.header_footer().has_footer(xlnt::header_footer::location::left)); xlnt_assert_equals(ws.header_footer().footer(xlnt::header_footer::location::left).plain_text(), "left && footer"); xlnt_assert(ws.header_footer().has_footer(xlnt::header_footer::location::center)); xlnt_assert_equals(ws.header_footer().footer(xlnt::header_footer::location::center).plain_text(), "center footer"); xlnt_assert(ws.header_footer().has_footer(xlnt::header_footer::location::right)); xlnt_assert_equals(ws.header_footer().footer(xlnt::header_footer::location::right).plain_text(), "right footer"); } void test_read_custom_properties() { xlnt::workbook wb; wb.load(path_helper::test_file("12_advanced_properties.xlsx")); xlnt_assert(wb.has_custom_property("Client")); xlnt_assert_equals(wb.custom_property("Client").get(), "me!"); } /// /// Read file as an XLSX-formatted ZIP file in the filesystem to a workbook, /// write the workbook back to memory, then ensure that the contents of the two files are equivalent. /// bool round_trip_matches_rw(const xlnt::path &source) { xlnt::workbook source_workbook; source_workbook.load(source); std::vector destination; source_workbook.save(destination); source_workbook.save("temp.xlsx"); #ifdef _MSC_VER std::ifstream source_stream(source.wstring(), std::ios::binary); #else std::ifstream source_stream(source.string(), std::ios::binary); #endif return xml_helper::xlsx_archives_match(xlnt::detail::to_vector(source_stream), destination); } bool round_trip_matches_rw(const xlnt::path &source, const std::string &password) { #ifdef _MSC_VER std::ifstream source_stream(source.wstring(), std::ios::binary); #else std::ifstream source_stream(source.string(), std::ios::binary); #endif auto source_data = xlnt::detail::to_vector(source_stream); xlnt::workbook source_workbook; source_workbook.load(source_data, password); std::vector destination_data; //source_workbook.save(destination_data, password); source_workbook.save("encrypted.xlsx", password); //xlnt::workbook temp; //temp.load("encrypted.xlsx", password); //TODO: finish implementing encryption and uncomment this //return source_data == destination_data; return true; } void test_round_trip_rw() { const auto files = std::vector { "2_minimal", "3_default", "4_every_style", u8"9_unicode_Λ", "10_comments_hyperlinks_formulae", "11_print_settings", "12_advanced_properties" }; for (const auto file : files) { auto path = path_helper::test_file(file + ".xlsx"); xlnt_assert(round_trip_matches_rw(path)); } } void test_round_trip_rw_encrypted() { const auto files = std::vector { "5_encrypted_agile", "6_encrypted_libre", "7_encrypted_standard", "8_encrypted_numbers" }; for (const auto file : files) { auto path = path_helper::test_file(file + ".xlsx"); auto password = std::string(file == "7_encrypted_standard" ? "password" : file == "6_encrypted_libre" ? u8"пароль" : "secret"); xlnt_assert(round_trip_matches_rw(path, password)); } } void test_streaming_read() { const auto path = path_helper::test_file("4_every_style.xlsx"); xlnt::streaming_workbook_reader reader; reader.open(xlnt::path(path)); for (auto sheet_name : reader.sheet_titles()) { reader.begin_worksheet(sheet_name); while (reader.has_cell()) { const auto cell = reader.read_cell(); std::cout << cell.reference().to_string() << " " << cell.to_string() << std::endl; } const auto ws = reader.end_worksheet(); } } void test_streaming_write() { const auto path = std::string("stream-out.xlsx"); xlnt::streaming_workbook_writer writer; writer.open(path); writer.add_worksheet("stream"); auto b2 = writer.add_cell("B2"); b2.value("B2!"); auto c3 = writer.add_cell("C3"); b2.value("should not change"); c3.value("C3!"); } };