diff --git a/libcockatrice_utility/CMakeLists.txt b/libcockatrice_utility/CMakeLists.txt index 3a81f179a..c6411ea76 100644 --- a/libcockatrice_utility/CMakeLists.txt +++ b/libcockatrice_utility/CMakeLists.txt @@ -5,8 +5,9 @@ set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) -set(UTILITY_SOURCES libcockatrice/utility/expression.cpp libcockatrice/utility/levenshtein.cpp - libcockatrice/utility/passwordhasher.cpp libcockatrice/utility/server_rate_limiter.cpp +set(UTILITY_SOURCES + libcockatrice/utility/expression.cpp libcockatrice/utility/levenshtein.cpp libcockatrice/utility/passwordhasher.cpp + libcockatrice/utility/server_rate_limiter.cpp libcockatrice/utility/warning_categories.cpp ) set(UTILITY_HEADERS @@ -24,6 +25,7 @@ set(UTILITY_HEADERS libcockatrice/utility/zone_names.h libcockatrice/utility/days_years_between.h libcockatrice/utility/server_rate_limiter.h + libcockatrice/utility/warning_categories.h ) add_library(libcockatrice_utility STATIC ${UTILITY_SOURCES} ${UTILITY_HEADERS}) diff --git a/libcockatrice_utility/libcockatrice/utility/warning_categories.cpp b/libcockatrice_utility/libcockatrice/utility/warning_categories.cpp new file mode 100644 index 000000000..b5ca563ff --- /dev/null +++ b/libcockatrice_utility/libcockatrice/utility/warning_categories.cpp @@ -0,0 +1,24 @@ +#include "warning_categories.h" + +QList parseWarningCategories(const QString &value) +{ + QList categories; + const QStringList entries = value.split(',', Qt::SkipEmptyParts); + for (const QString &entry : entries) { + const QStringList parts = entry.split('|'); + WarningCategory category; + category.name = parts.first().trimmed(); + if (category.name.isEmpty()) { + continue; + } + if (parts.size() > 1) { + bool ok = false; + const int il = parts.at(1).trimmed().toInt(&ok); + if (ok && il > 0) { + category.startingIl = il; + } + } + categories.append(category); + } + return categories; +} diff --git a/libcockatrice_utility/libcockatrice/utility/warning_categories.h b/libcockatrice_utility/libcockatrice/utility/warning_categories.h new file mode 100644 index 000000000..ce5308cf4 --- /dev/null +++ b/libcockatrice_utility/libcockatrice/utility/warning_categories.h @@ -0,0 +1,28 @@ +#ifndef WARNING_CATEGORIES_H +#define WARNING_CATEGORIES_H + +#include +#include + +/** + * A warning category the server offers to moderators, optionally carrying a + * recommended starting intervention level (see the moderator guide). + */ +struct WarningCategory +{ + QString name; + int startingIl = 1; +}; + +/** + * Parses the `server/officialwarnings` setting value into warning categories. + * + * Entries are separated by commas. Each entry is a category name, optionally + * followed by "|" and the recommended starting intervention level: + * "Abusive Language|1,Cheating|2,Spamming" + * Entries without an explicit level default to intervention level 1. + * Empty entries are skipped. + */ +QList parseWarningCategories(const QString &value); + +#endif // WARNING_CATEGORIES_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 51809912b..804293784 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,6 +11,7 @@ add_test(NAME playmat_resolver_test COMMAND playmat_resolver_test) add_test(NAME server_card_counter_test COMMAND server_card_counter_test) add_test(NAME server_counter_test COMMAND server_counter_test) add_test(NAME server_rate_limiter_test COMMAND server_rate_limiter_test) +add_test(NAME warning_categories_test COMMAND warning_categories_test) add_test(NAME deck_hash_performance_test COMMAND deck_hash_performance_test) set_tests_properties(deck_hash_performance_test PROPERTIES TIMEOUT 5) @@ -27,6 +28,7 @@ add_executable(deck_hash_performance_test deck_hash_performance_test.cpp) add_executable(server_card_counter_test server_card_counter_test.cpp) add_executable(server_counter_test server_counter_test.cpp) add_executable(server_rate_limiter_test server_rate_limiter_test.cpp) +add_executable(warning_categories_test warning_categories_test.cpp) find_package(GTest) @@ -63,6 +65,7 @@ if(NOT GTEST_FOUND) add_dependencies(server_card_counter_test gtest) add_dependencies(server_counter_test gtest) add_dependencies(server_rate_limiter_test gtest) + add_dependencies(warning_categories_test gtest) endif() include_directories(${GTEST_INCLUDE_DIRS}) @@ -94,6 +97,9 @@ target_link_libraries( target_link_libraries( server_rate_limiter_test libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES} ) +target_link_libraries( + warning_categories_test libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES} +) add_subdirectory(card_zone_algorithms) add_subdirectory(carddatabase) diff --git a/tests/warning_categories_test.cpp b/tests/warning_categories_test.cpp new file mode 100644 index 000000000..432885dd9 --- /dev/null +++ b/tests/warning_categories_test.cpp @@ -0,0 +1,89 @@ +#include "gtest/gtest.h" +#include +#include + +TEST(WarningCategoriesTest, EmptyValueYieldsNoCategories) +{ + EXPECT_TRUE(parseWarningCategories(QString()).isEmpty()); + EXPECT_TRUE(parseWarningCategories(QString("")).isEmpty()); +} + +TEST(WarningCategoriesTest, PlainNamesDefaultToInterventionLevelOne) +{ + const QList categories = parseWarningCategories("Flaming,Spamming,Causing Drama"); + + ASSERT_EQ(3, categories.size()); + EXPECT_EQ("Flaming", categories.at(0).name); + EXPECT_EQ(1, categories.at(0).startingIl); + EXPECT_EQ("Spamming", categories.at(1).name); + EXPECT_EQ(1, categories.at(1).startingIl); + EXPECT_EQ("Causing Drama", categories.at(2).name); + EXPECT_EQ(1, categories.at(2).startingIl); +} + +TEST(WarningCategoriesTest, ExplicitInterventionLevelsAreParsed) +{ + const QList categories = parseWarningCategories("Cheating|2,Inappropriate Avatar|3"); + + ASSERT_EQ(2, categories.size()); + EXPECT_EQ("Cheating", categories.at(0).name); + EXPECT_EQ(2, categories.at(0).startingIl); + EXPECT_EQ("Inappropriate Avatar", categories.at(1).name); + EXPECT_EQ(3, categories.at(1).startingIl); +} + +TEST(WarningCategoriesTest, MixedEntriesKeepDefaultsForThoseWithoutLevels) +{ + const QList categories = parseWarningCategories("Abusive Language|1,Cheating|2,Spamming"); + + ASSERT_EQ(3, categories.size()); + EXPECT_EQ(1, categories.at(0).startingIl); + EXPECT_EQ(2, categories.at(1).startingIl); + EXPECT_EQ("Spamming", categories.at(2).name); + EXPECT_EQ(1, categories.at(2).startingIl); +} + +TEST(WarningCategoriesTest, EmptyEntriesAreSkipped) +{ + const QList categories = parseWarningCategories("Spamming,,Cheating|2,"); + + ASSERT_EQ(2, categories.size()); + EXPECT_EQ("Spamming", categories.at(0).name); + EXPECT_EQ("Cheating", categories.at(1).name); +} + +TEST(WarningCategoriesTest, WhitespaceIsTrimmed) +{ + const QList categories = parseWarningCategories(" Abusive Language , Cheating | 2 "); + + ASSERT_EQ(2, categories.size()); + EXPECT_EQ("Abusive Language", categories.at(0).name); + EXPECT_EQ(1, categories.at(0).startingIl); + EXPECT_EQ("Cheating", categories.at(1).name); + EXPECT_EQ(2, categories.at(1).startingIl); +} + +TEST(WarningCategoriesTest, InvalidInterventionLevelsFallBackToOne) +{ + const QList categories = parseWarningCategories("Spamming|abc,Cheating|0,Targeted Harassment|-3"); + + ASSERT_EQ(3, categories.size()); + EXPECT_EQ(1, categories.at(0).startingIl); + EXPECT_EQ(1, categories.at(1).startingIl); + EXPECT_EQ(1, categories.at(2).startingIl); +} + +TEST(WarningCategoriesTest, EntryWithOnlyLevelIsSkipped) +{ + const QList categories = parseWarningCategories("|2,Spamming|2"); + + ASSERT_EQ(1, categories.size()); + EXPECT_EQ("Spamming", categories.at(0).name); + EXPECT_EQ(2, categories.at(0).startingIl); +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}