diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index d4393cf9c7..bd391f2bae 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -23,9 +23,15 @@ #include #include +namespace { + /** + * @brief Thread emoji - https://www.compart.com/en/unicode/U+1F9F5 + */ + inline const std::string THREAD_EMOJI = "\xF0\x9F\xA7\xB5"; +} /* Unit tests go here */ -int main() +int main(int argc, char *argv[]) { std::string token(get_token()); @@ -33,7 +39,9 @@ int main() if (offline) { std::cout << "Running offline unit tests only.\n"; } else { - std::cout << "Running offline and online unit tests. Guild ID: " << TEST_GUILD_ID << " Text Channel ID: " << TEST_TEXT_CHANNEL_ID << " VC ID: " << TEST_VC_ID << " User ID: " << TEST_USER_ID << " Event ID: " << TEST_EVENT_ID << "\n"; + if (argc > 1 && std::find_if(argv + 1, argv + argc, [](const char *a){ return (std::strcmp(a, "full") == 0); }) != argv + argc) + extended = true; + std::cout << "Running offline and " << (extended ? "extended" : "limited") << " online unit tests. Guild ID: " << TEST_GUILD_ID << " Text Channel ID: " << TEST_TEXT_CHANNEL_ID << " VC ID: " << TEST_VC_ID << " User ID: " << TEST_USER_ID << " Event ID: " << TEST_EVENT_ID << "\n"; } std::string test_to_escape = "*** _This is a test_ ***\n```cpp\n\ @@ -800,6 +808,10 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b } void test_pin() { + if (!extended) { + set_pin_tested(); + return; + } set_test("MESSAGEPIN", false); set_test("MESSAGEUNPIN", false); bot.message_pin(channel_id, message_id, [=](const dpp::confirmation_callback_t &callback) { @@ -859,12 +871,13 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b bool members_tested = false; bool messages_tested = false; bool events_tested = false; + bool get_active_tested = false; uint32_t events_tested_mask = 0; uint32_t events_to_test_mask = 0; void delete_if_done() { - if (edit_tested && members_tested && messages_tested && events_tested) { + if (edit_tested && members_tested && messages_tested && events_tested && get_active_tested) { bot.channel_delete(thread_id); } } @@ -896,6 +909,15 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b delete_if_done(); } + void set_get_active_tested() + { + if (get_active_tested) { + return; + } + get_active_tested = true; + delete_if_done(); + } + void set_messages_tested() { if (messages_tested) { @@ -912,7 +934,7 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b } events_tested_mask |= flag; for (uint32_t i = 1; i < EVENT_END; i <<= 1) { - if ((events_tested_mask & i) != i) + if ((events_to_test_mask & i) && (events_tested_mask & i) != i) return; } set_events_tested(); @@ -961,17 +983,42 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b } } - void test_members(const dpp::thread &thread) - { + void test_get_active(const dpp::thread &thread) + { std::lock_guard lock{mutex}; - set_test("THREAD_MEMBER_ADD", false); - set_test("THREAD_MEMBER_GET", false); - set_test("THREAD_MEMBERS_GET", false); - set_test("THREAD_MEMBER_REMOVE", false); - set_test("THREAD_MEMBERS_ADD_EVENT", false); - set_test("THREAD_MEMBERS_REMOVE_EVENT", false); + set_test("THREAD_GET_ACTIVE", false); + bot.threads_get_active(TEST_GUILD_ID, [this](const dpp::confirmation_callback_t &callback) { + std::lock_guard lock{mutex}; + if (!callback.is_error()) { + const auto &threads = callback.get(); + if (auto thread_it = threads.find(thread_id); thread_it != threads.end()) { + const auto &thread = thread_it->second.active_thread; + const auto &member = thread_it->second.bot_member; + if (thread.id == thread_id && member.has_value() && member->user_id == bot.me.id) { + set_test("THREAD_GET_ACTIVE", true); + } + } + } + set_get_active_tested(); + }); + } + + void test_members(const dpp::thread &thread) + { + std::lock_guard lock{mutex}; + if (!members_tested) { + if (!extended) { + set_members_tested(); + return; + } + set_test("THREAD_MEMBER_ADD", false); + set_test("THREAD_MEMBER_GET", false); + set_test("THREAD_MEMBERS_GET", false); + set_test("THREAD_MEMBER_REMOVE", false); + set_test("THREAD_MEMBERS_ADD_EVENT", false); + set_test("THREAD_MEMBERS_REMOVE_EVENT", false); bot.thread_member_add(thread_id, TEST_USER_ID, [this](const dpp::confirmation_callback_t &callback) { std::lock_guard lock{mutex}; if (callback.is_error()) { @@ -998,34 +1045,27 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b return; } set_test("THREAD_MEMBERS_GET", true); - bot.threads_get_active(TEST_GUILD_ID, [this](const dpp::confirmation_callback_t &callback) { + bot.thread_member_remove(thread_id, TEST_USER_ID, [this](const dpp::confirmation_callback_t &callback) { std::lock_guard lock{mutex}; if (!callback.is_error()) { - const auto &threads = callback.get(); - if (auto thread_it = threads.find(thread_id); thread_it != threads.end()) { - const auto &thread = thread_it->second.active_thread; - const auto &member = thread_it->second.bot_member; - if (thread.id == thread_id && member.has_value() && member->user_id == bot.me.id) { - set_test("THREAD_GET_ACTIVE", true); - } - } + set_test("THREAD_MEMBER_REMOVE", true); } - bot.thread_member_remove(thread_id, TEST_USER_ID, [this](const dpp::confirmation_callback_t &callback) { - std::lock_guard lock{mutex}; - if (!callback.is_error()) { - set_test("THREAD_MEMBER_REMOVE", true); - } - set_members_tested(); - }); + set_members_tested(); }); }); }); }); } - } + } void test_messages(const dpp::thread &thread) { + if (!extended) { + set_messages_tested(); + set_events_tested(); + return; + } + std::lock_guard lock{mutex}; set_test("THREAD_MESSAGE", false); set_test("THREAD_MESSAGE_CREATE_EVENT", false); @@ -1083,14 +1123,10 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b }); } - void confirm_message_receive() - { - std::lock_guard lock{mutex}; - } - void run(const dpp::thread &thread) { thread_id = thread.id; + test_get_active(thread); test_edit(thread); test_members(thread); test_messages(thread); @@ -1357,18 +1393,25 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b } bot.invite_get(created.code, [&bot, created](const dpp::confirmation_callback_t &event) { - if (event.is_error()) return; + if (!event.is_error()) { + auto retrieved = event.get(); + if (retrieved.code == created.code && retrieved.guild_id == created.guild_id && retrieved.channel_id == created.channel_id && retrieved.inviter.id == created.inviter.id) { + if (retrieved.destination_guild.flags & dpp::g_community) + set_test("INVITE_GET", retrieved.expires_at == 0); + else + set_test("INVITE_GET", true); - auto retrieved = event.get(); - if (retrieved.code == created.code && retrieved.expires_at == 0 && retrieved.guild_id == created.guild_id && retrieved.channel_id == created.channel_id && retrieved.inviter.id == created.inviter.id) { - set_test("INVITE_GET", true); + } + else { + set_test("INVITE_GET", false); + } + } else { + set_test("INVITE_GET", false); } set_test("INVITE_DELETE_EVENT", false); - bot.invite_delete(retrieved.code, [](const dpp::confirmation_callback_t &event) { - if (!event.is_error()) { - set_test("INVITE_DELETE", true); - } + bot.invite_delete(created.code, [](const dpp::confirmation_callback_t &event) { + set_test("INVITE_DELETE", !event.is_error()); }); }); }); diff --git a/src/unittest/test.h b/src/unittest/test.h index 331002a997..317996a938 100644 --- a/src/unittest/test.h +++ b/src/unittest/test.h @@ -40,6 +40,8 @@ enum test_type_t { tt_offline, /* A test that requires discord connectivity */ tt_online, + /* A test that requires both online and full tests to be enabled */ + tt_extended }; /* Represents a test case */ @@ -75,6 +77,7 @@ extern dpp::snowflake TEST_EVENT_ID; /* True if we skip tt_online tests */ extern bool offline; +extern bool extended; /** * @brief Perform a test of a REST base API call with one parameter @@ -314,8 +317,3 @@ inline constexpr user_project_id_t get_user_snowflake; inline constexpr auto is_owner = [](auto &&user) noexcept { return get_user_snowflake(user) == TEST_USER_ID; }; - -/** - * @brief Thread emoji - https://www.compart.com/en/unicode/U+1F9F5 - */ -inline const std::string THREAD_EMOJI = "\xF0\x9F\xA7\xB5"; diff --git a/src/unittest/unittest.cpp b/src/unittest/unittest.cpp index 70c9b53788..6c909c9f77 100644 --- a/src/unittest/unittest.cpp +++ b/src/unittest/unittest.cpp @@ -33,8 +33,6 @@ std::map tests = { {"MESSAGECREATE", {tt_online, "Creation of a channel message", false, false}}, {"MESSAGEEDIT", {tt_online, "Editing a channel message", false, false}}, {"EDITEVENT", {tt_online, "Message edit event", false, false}}, - {"MESSAGEPIN", {tt_online, "Pinning a channel message", false, false}}, - {"MESSAGEUNPIN", {tt_online, "Unpinning a channel message", false, false}}, {"MESSAGEDELETE", {tt_online, "Deletion of a channel message", false, false}}, {"MESSAGERECEIVE", {tt_online, "Receipt of a created message", false, false}}, {"MESSAGEFILE", {tt_online, "Message attachment send and check", false, false}}, @@ -103,22 +101,8 @@ std::map tests = { {"THREAD_DELETE_EVENT", {tt_online, "cluster::on_thread_delete event", false, false}}, {"THREAD_EDIT", {tt_online, "cluster::thread_edit", false, false}}, {"THREAD_UPDATE_EVENT", {tt_online, "cluster::on_thread_update event", false, false}}, - {"THREAD_MEMBER_ADD", {tt_online, "cluster::thread_member_add", false, false}}, - {"THREAD_MEMBER_GET", {tt_online, "cluster::thread_member_get", false, false}}, - {"THREAD_MEMBERS_GET", {tt_online, "cluster::thread_members_get", false, false}}, - {"THREAD_MEMBER_REMOVE", {tt_online, "cluster::thread_member_remove", false, false}}, - {"THREAD_MEMBERS_ADD_EVENT", {tt_online, "cluster::on_thread_members_update event with member addition", false, false}}, - {"THREAD_MEMBERS_REMOVE_EVENT", {tt_online, "cluster::on_thread_members_update event with member removal", false, false}}, - {"THREAD_CREATE_MESSAGE", {tt_online, "cluster::thread_create_with_message", false, false}}, {"THREAD_GET_ACTIVE", {tt_online, "cluster::threads_get_active", false, false}}, - {"THREAD_MESSAGE", {tt_online, "message manipulation in thread", false, false}}, - {"THREAD_MESSAGE_CREATE_EVENT", {tt_online, "cluster::on_message_create in thread", false, false}}, - {"THREAD_MESSAGE_EDIT_EVENT", {tt_online, "cluster::on_message_edit in thread", false, false}}, - {"THREAD_MESSAGE_DELETE_EVENT", {tt_online, "cluster::on_message_delete in thread", false, false}}, - {"THREAD_MESSAGE_REACT_ADD_EVENT", {tt_online, "cluster::on_reaction_add in thread", false, false}}, - {"THREAD_MESSAGE_REACT_REMOVE_EVENT", {tt_online, "cluster::on_reaction_remove in thread", false, false}}, - {"VOICE_CHANNEL_CREATE", {tt_online, "creating a voice channel", false, false}}, {"VOICE_CHANNEL_EDIT", {tt_online, "editing the created voice channel", false, false}}, {"VOICE_CHANNEL_DELETE", {tt_online, "deleting the created voice channel", false, false}}, @@ -169,10 +153,31 @@ std::map tests = { {"INVITE_CREATE", {tt_online, "cluster::channel_invite_create", false, false}}, {"INVITE_GET", {tt_online, "cluster::invite_get", false, false}}, {"INVITE_DELETE", {tt_online, "cluster::invite_delete", false, false}}, + + /* Extended set -- Less important, skipped on the master branch due to rate limits and GitHub actions limitations*/ + /* To execute, run unittests with "full" command line argument */ + {"MESSAGEPIN", {tt_extended, "Pinning a channel message", false, false}}, + {"MESSAGEUNPIN", {tt_extended, "Unpinning a channel message", false, false}}, + + {"THREAD_MEMBER_ADD", {tt_extended, "cluster::thread_member_add", false, false}}, + {"THREAD_MEMBER_GET", {tt_extended, "cluster::thread_member_get", false, false}}, + {"THREAD_MEMBERS_GET", {tt_extended, "cluster::thread_members_get", false, false}}, + {"THREAD_MEMBER_REMOVE", {tt_extended, "cluster::thread_member_remove", false, false}}, + {"THREAD_MEMBERS_ADD_EVENT", {tt_extended, "cluster::on_thread_members_update event with member addition", false, false}}, + {"THREAD_MEMBERS_REMOVE_EVENT", {tt_extended, "cluster::on_thread_members_update event with member removal", false, false}}, + {"THREAD_CREATE_MESSAGE", {tt_extended, "cluster::thread_create_with_message", false, false}}, + + {"THREAD_MESSAGE", {tt_extended, "message manipulation in thread", false, false}}, + {"THREAD_MESSAGE_CREATE_EVENT", {tt_extended, "cluster::on_message_create in thread", false, false}}, + {"THREAD_MESSAGE_EDIT_EVENT", {tt_extended, "cluster::on_message_edit in thread", false, false}}, + {"THREAD_MESSAGE_DELETE_EVENT", {tt_extended, "cluster::on_message_delete in thread", false, false}}, + {"THREAD_MESSAGE_REACT_ADD_EVENT", {tt_extended, "cluster::on_reaction_add in thread", false, false}}, + {"THREAD_MESSAGE_REACT_REMOVE_EVENT", {tt_extended, "cluster::on_reaction_remove in thread", false, false}}, }; double start = dpp::utility::time_f(); bool offline = false; +bool extended = false; dpp::snowflake TEST_GUILD_ID = std::stoull(SAFE_GETENV("TEST_GUILD_ID")); dpp::snowflake TEST_TEXT_CHANNEL_ID = std::stoull(SAFE_GETENV("TEST_TEXT_CHANNEL_ID")); @@ -216,7 +221,7 @@ int test_summary() { std::cout << "\u001b[37;1m\n\nUNIT TEST SUMMARY\n==================\n\u001b[0m"; for (auto & t : tests) { bool test_skipped = false; - if (t.second.type == tt_online && offline) { + if ((t.second.type == tt_online && offline) || (t.second.type == tt_extended && !extended)) { skipped++; test_skipped = true; } else { @@ -232,9 +237,18 @@ int test_summary() { return failed; } +namespace { + std::string get_testdata_dir() { + char *env_var = getenv("TEST_DATA_DIR"); + + return (env_var ? env_var : "../../testdata/"); + } +} + std::vector load_test_audio() { std::vector testaudio; - std::ifstream input ("../../testdata/Robot.pcm", std::ios::in|std::ios::binary|std::ios::ate); + std::string dir = get_testdata_dir(); + std::ifstream input (dir + "Robot.pcm", std::ios::in|std::ios::binary|std::ios::ate); if (input.is_open()) { size_t testaudio_size = input.tellg(); testaudio.resize(testaudio_size); @@ -243,7 +257,7 @@ std::vector load_test_audio() { input.close(); } else { - std::cout << "ERROR: Can't load ../../testdata/Robot.pcm\n"; + std::cout << "ERROR: Can't load " + dir + "Robot.pcm\n"; exit(1); } return testaudio; @@ -251,7 +265,8 @@ std::vector load_test_audio() { std::vector load_test_image() { std::vector testimage; - std::ifstream input ("../../testdata/DPP-Logo.png", std::ios::in|std::ios::binary|std::ios::ate); + std::string dir = get_testdata_dir(); + std::ifstream input (dir + "DPP-Logo.png", std::ios::in|std::ios::binary|std::ios::ate); if (input.is_open()) { size_t testimage_size = input.tellg(); testimage.resize(testimage_size); @@ -260,7 +275,7 @@ std::vector load_test_image() { input.close(); } else { - std::cout << "ERROR: Can't load ../../testdata/DPP-Logo.png\n"; + std::cout << "ERROR: Can't load " + dir + "DPP-Logo.png\n"; exit(1); } return testimage; @@ -287,9 +302,9 @@ void wait_for_tests() { for (auto & t : tests) { if (t.second.executed == true) { executed++; - } else if (offline && t.second.type == tt_online && !t.second.executed) { + } else if (!t.second.executed && ((offline && t.second.type == tt_online) || (!extended && t.second.type == tt_extended))) { executed++; - t.second.success = true; + t.second.executed = true; std::cout << "[" << std::fixed << std::setprecision(3) << get_time() << "]: " << "[\u001b[33mSKIPPED\u001b[0m] " << t.second.description << "\n"; } } @@ -300,4 +315,8 @@ void wait_for_tests() { std::this_thread::sleep_for(std::chrono::seconds(1)); ticks++; } + for (auto &t : tests) { + if (!t.second.executed) + std::cout << "[" << std::fixed << std::setprecision(3) << get_time() << "]: " << "[\u001b[31mTIMEOUT\u001b[0m] " << t.second.description << "\n"; + } }