Make tests more platform agnostic (#4650)

Use QTemporaryDir to create the test directory
Add option to use httpbin over local docker
Increase delay for opening a connection

Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
nerix 2023-05-26 14:54:23 +02:00 committed by GitHub
parent 2264c44f10
commit 1bc423d9c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 56 additions and 24 deletions

View file

@ -12,6 +12,7 @@
#include <benchmark/benchmark.h> #include <benchmark/benchmark.h>
#include <QDebug> #include <QDebug>
#include <QString> #include <QString>
#include <QTemporaryDir>
using namespace chatterino; using namespace chatterino;
@ -66,7 +67,8 @@ public:
static void BM_HighlightTest(benchmark::State &state) static void BM_HighlightTest(benchmark::State &state)
{ {
MockApplication mockApplication; MockApplication mockApplication;
Settings settings("/tmp/c2-mock"); QTemporaryDir settingsDir;
Settings settings(settingsDir.path());
std::string message = std::string message =
R"(@badge-info=subscriber/34;badges=moderator/1,subscriber/24;color=#FF0000;display-name=테스트계정420;emotes=41:6-13,15-22;flags=;id=a3196c7e-be4c-4b49-9c5a-8b8302b50c2a;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1590922213730;turbo=0;user-id=117166826;user-type=mod :testaccount_420!testaccount_420@testaccount_420.tmi.twitch.tv PRIVMSG #pajlada :-tags Kreygasm,Kreygasm (no space))"; R"(@badge-info=subscriber/34;badges=moderator/1,subscriber/24;color=#FF0000;display-name=테스트계정420;emotes=41:6-13,15-22;flags=;id=a3196c7e-be4c-4b49-9c5a-8b8302b50c2a;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1590922213730;turbo=0;user-id=117166826;user-type=mod :testaccount_420!testaccount_420@testaccount_420.tmi.twitch.tv PRIVMSG #pajlada :-tags Kreygasm,Kreygasm (no space))";

View file

@ -3,6 +3,7 @@
#include <benchmark/benchmark.h> #include <benchmark/benchmark.h>
#include <QApplication> #include <QApplication>
#include <QtConcurrent> #include <QtConcurrent>
#include <QTemporaryDir>
using namespace chatterino; using namespace chatterino;
@ -12,12 +13,15 @@ int main(int argc, char **argv)
::benchmark::Initialize(&argc, argv); ::benchmark::Initialize(&argc, argv);
// Ensure settings are initialized before any tests are run // Ensure settings are initialized before any benchmarks are run
chatterino::Settings settings("/tmp/c2-empty-mock"); QTemporaryDir settingsDir;
settingsDir.setAutoRemove(false); // we'll remove it manually
chatterino::Settings settings(settingsDir.path());
QtConcurrent::run([&app] { QtConcurrent::run([&app, &settingsDir]() mutable {
::benchmark::RunSpecifiedBenchmarks(); ::benchmark::RunSpecifiedBenchmarks();
settingsDir.remove();
app.exit(0); app.exit(0);
}); });

View file

@ -1,5 +1,7 @@
project(chatterino-test) project(chatterino-test)
option(CHATTERINO_TEST_USE_PUBLIC_HTTPBIN "Use public httpbin for testing network requests" OFF)
set(test_SOURCES set(test_SOURCES
${CMAKE_CURRENT_LIST_DIR}/src/main.cpp ${CMAKE_CURRENT_LIST_DIR}/src/main.cpp
${CMAKE_CURRENT_LIST_DIR}/src/ChannelChatters.cpp ${CMAKE_CURRENT_LIST_DIR}/src/ChannelChatters.cpp
@ -54,6 +56,10 @@ if(CHATTERINO_ENABLE_LTO)
PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
endif() endif()
if(CHATTERINO_TEST_USE_PUBLIC_HTTPBIN)
target_compile_definitions(${PROJECT_NAME} PRIVATE CHATTERINO_TEST_USE_PUBLIC_HTTPBIN)
endif()
# gtest_add_tests manages to discover the tests because it looks through the source files # gtest_add_tests manages to discover the tests because it looks through the source files
# HOWEVER, it fails to run, because we have some bug that causes the QApplication exit to stall when no network requests have been made. # HOWEVER, it fails to run, because we have some bug that causes the QApplication exit to stall when no network requests have been made.
# ctest runs each test individually, so for now we require that testers just run the ./bin/chatterino-test binary without any filters applied # ctest runs each test individually, so for now we require that testers just run the ./bin/chatterino-test binary without any filters applied

View file

@ -19,6 +19,7 @@
#include <QDir> #include <QDir>
#include <QFile> #include <QFile>
#include <QString> #include <QString>
#include <QTemporaryDir>
using namespace chatterino; using namespace chatterino;
using ::testing::Exactly; using ::testing::Exactly;
@ -178,9 +179,9 @@ protected:
void SetUp() override void SetUp() override
{ {
// Write default settings to the mock settings json file // Write default settings to the mock settings json file
ASSERT_TRUE(QDir().mkpath("/tmp/c2-tests")); this->settingsDir_ = std::make_unique<QTemporaryDir>();
QFile settingsFile("/tmp/c2-tests/settings.json"); QFile settingsFile(this->settingsDir_->filePath("settings.json"));
ASSERT_TRUE(settingsFile.open(QIODevice::WriteOnly | QIODevice::Text)); ASSERT_TRUE(settingsFile.open(QIODevice::WriteOnly | QIODevice::Text));
ASSERT_GT(settingsFile.write(DEFAULT_SETTINGS.toUtf8()), 0); ASSERT_GT(settingsFile.write(DEFAULT_SETTINGS.toUtf8()), 0);
ASSERT_TRUE(settingsFile.flush()); ASSERT_TRUE(settingsFile.flush());
@ -194,7 +195,7 @@ protected:
EXPECT_CALL(*this->mockHelix, update).Times(Exactly(1)); EXPECT_CALL(*this->mockHelix, update).Times(Exactly(1));
this->mockApplication = std::make_unique<MockApplication>(); this->mockApplication = std::make_unique<MockApplication>();
this->settings = std::make_unique<Settings>("/tmp/c2-tests"); this->settings = std::make_unique<Settings>(this->settingsDir_->path());
this->paths = std::make_unique<Paths>(); this->paths = std::make_unique<Paths>();
this->controller = std::make_unique<HighlightController>(); this->controller = std::make_unique<HighlightController>();
@ -206,16 +207,19 @@ protected:
void TearDown() override void TearDown() override
{ {
ASSERT_TRUE(QDir("/tmp/c2-tests").removeRecursively());
this->mockApplication.reset(); this->mockApplication.reset();
this->settings.reset(); this->settings.reset();
this->paths.reset(); this->paths.reset();
this->controller.reset(); this->controller.reset();
this->settingsDir_.reset();
delete this->mockHelix; delete this->mockHelix;
} }
std::unique_ptr<QTemporaryDir> settingsDir_;
std::unique_ptr<MockApplication> mockApplication; std::unique_ptr<MockApplication> mockApplication;
std::unique_ptr<Settings> settings; std::unique_ptr<Settings> settings;
std::unique_ptr<Paths> paths; std::unique_ptr<Paths> paths;

View file

@ -18,6 +18,7 @@
#include <QFile> #include <QFile>
#include <QModelIndex> #include <QModelIndex>
#include <QString> #include <QString>
#include <QTemporaryDir>
namespace { namespace {
@ -122,9 +123,9 @@ protected:
void SetUp() override void SetUp() override
{ {
// Write default settings to the mock settings json file // Write default settings to the mock settings json file
ASSERT_TRUE(QDir().mkpath("/tmp/c2-tests")); this->settingsDir_ = std::make_unique<QTemporaryDir>();
QFile settingsFile("/tmp/c2-tests/settings.json"); QFile settingsFile(this->settingsDir_->filePath("settings.json"));
ASSERT_TRUE(settingsFile.open(QIODevice::WriteOnly | QIODevice::Text)); ASSERT_TRUE(settingsFile.open(QIODevice::WriteOnly | QIODevice::Text));
ASSERT_GT(settingsFile.write(DEFAULT_SETTINGS.toUtf8()), 0); ASSERT_GT(settingsFile.write(DEFAULT_SETTINGS.toUtf8()), 0);
ASSERT_TRUE(settingsFile.flush()); ASSERT_TRUE(settingsFile.flush());
@ -137,7 +138,7 @@ protected:
EXPECT_CALL(*this->mockHelix, update).Times(Exactly(1)); EXPECT_CALL(*this->mockHelix, update).Times(Exactly(1));
this->mockApplication = std::make_unique<MockApplication>(); this->mockApplication = std::make_unique<MockApplication>();
this->settings = std::make_unique<Settings>("/tmp/c2-tests"); this->settings = std::make_unique<Settings>(this->settingsDir_->path());
this->paths = std::make_unique<Paths>(); this->paths = std::make_unique<Paths>();
this->mockApplication->accounts.initialize(*this->settings, this->mockApplication->accounts.initialize(*this->settings,
@ -153,15 +154,18 @@ protected:
void TearDown() override void TearDown() override
{ {
ASSERT_TRUE(QDir("/tmp/c2-tests").removeRecursively());
this->mockApplication.reset(); this->mockApplication.reset();
this->settings.reset(); this->settings.reset();
this->paths.reset(); this->paths.reset();
this->mockHelix.reset(); this->mockHelix.reset();
this->completionModel.reset(); this->completionModel.reset();
this->channelPtr.reset(); this->channelPtr.reset();
this->settingsDir_.reset();
} }
std::unique_ptr<QTemporaryDir> settingsDir_;
std::unique_ptr<MockApplication> mockApplication; std::unique_ptr<MockApplication> mockApplication;
std::unique_ptr<Settings> settings; std::unique_ptr<Settings> settings;
std::unique_ptr<Paths> paths; std::unique_ptr<Paths> paths;
@ -222,8 +226,14 @@ protected:
TEST_F(InputCompletionTest, EmoteNameFiltering) TEST_F(InputCompletionTest, EmoteNameFiltering)
{ {
// The completion doesn't guarantee an ordering for a specific category of emotes.
// This tests a specific implementation of the underlying std::unordered_map,
// so depending on the standard library used when compiling, this might yield
// different results.
auto completion = queryEmoteCompletion(":feels"); auto completion = queryEmoteCompletion(":feels");
ASSERT_EQ(completion.size(), 3); ASSERT_EQ(completion.size(), 3);
// all these matches are BTTV global emotes
ASSERT_EQ(completion[0].displayName, "FeelsBirthdayMan"); ASSERT_EQ(completion[0].displayName, "FeelsBirthdayMan");
ASSERT_EQ(completion[1].displayName, "FeelsBadMan"); ASSERT_EQ(completion[1].displayName, "FeelsBadMan");
ASSERT_EQ(completion[2].displayName, "FeelsGoodMan"); ASSERT_EQ(completion[2].displayName, "FeelsGoodMan");
@ -231,6 +241,7 @@ TEST_F(InputCompletionTest, EmoteNameFiltering)
completion = queryEmoteCompletion(":)"); completion = queryEmoteCompletion(":)");
ASSERT_EQ(completion.size(), 3); ASSERT_EQ(completion.size(), 3);
ASSERT_EQ(completion[0].displayName, ":)"); // Exact match with : prefix ASSERT_EQ(completion[0].displayName, ":)"); // Exact match with : prefix
// all these matches are Twitch global emotes
ASSERT_EQ(completion[1].displayName, ":-)"); ASSERT_EQ(completion[1].displayName, ":-)");
ASSERT_EQ(completion[2].displayName, "B-)"); ASSERT_EQ(completion[2].displayName, "B-)");

View file

@ -12,8 +12,11 @@ using namespace chatterino;
namespace { namespace {
// Change to http://httpbin.org if you don't want to run the docker image yourself to test this #ifdef CHATTERINO_TEST_USE_PUBLIC_HTTPBIN
const char *const HTTPBIN_BASE_URL = "http://httpbin.org";
#else
const char *const HTTPBIN_BASE_URL = "http://127.0.0.1:9051"; const char *const HTTPBIN_BASE_URL = "http://127.0.0.1:9051";
#endif
QString getStatusURL(int code) QString getStatusURL(int code)
{ {

View file

@ -48,7 +48,7 @@ TEST(TwitchPubSubClient, ServerRespondsToPings)
pubSub->listenToTopic("test"); pubSub->listenToTopic("test");
std::this_thread::sleep_for(50ms); std::this_thread::sleep_for(150ms);
ASSERT_EQ(pubSub->diag.connectionsOpened, 1); ASSERT_EQ(pubSub->diag.connectionsOpened, 1);
ASSERT_EQ(pubSub->diag.connectionsClosed, 0); ASSERT_EQ(pubSub->diag.connectionsClosed, 0);
@ -211,7 +211,7 @@ TEST(TwitchPubSubClient, ExceedTopicLimitSingleStep)
pubSub->listenToTopic("test"); pubSub->listenToTopic("test");
} }
std::this_thread::sleep_for(50ms); std::this_thread::sleep_for(150ms);
ASSERT_EQ(pubSub->diag.connectionsOpened, 2); ASSERT_EQ(pubSub->diag.connectionsOpened, 2);
ASSERT_EQ(pubSub->diag.connectionsClosed, 0); ASSERT_EQ(pubSub->diag.connectionsClosed, 0);
@ -242,7 +242,7 @@ TEST(TwitchPubSubClient, ReceivedWhisper)
pubSub->listenToTopic("whispers.123456"); pubSub->listenToTopic("whispers.123456");
std::this_thread::sleep_for(50ms); std::this_thread::sleep_for(150ms);
ASSERT_EQ(pubSub->diag.connectionsOpened, 1); ASSERT_EQ(pubSub->diag.connectionsOpened, 1);
ASSERT_EQ(pubSub->diag.connectionsClosed, 0); ASSERT_EQ(pubSub->diag.connectionsClosed, 0);
@ -324,7 +324,7 @@ TEST(TwitchPubSubClient, MissingToken)
pubSub->listenToTopic("chat_moderator_actions.123456.123456"); pubSub->listenToTopic("chat_moderator_actions.123456.123456");
std::this_thread::sleep_for(50ms); std::this_thread::sleep_for(150ms);
ASSERT_EQ(pubSub->diag.connectionsOpened, 1); ASSERT_EQ(pubSub->diag.connectionsOpened, 1);
ASSERT_EQ(pubSub->diag.connectionsClosed, 0); ASSERT_EQ(pubSub->diag.connectionsClosed, 0);

View file

@ -1,14 +1,10 @@
#include "common/NetworkManager.hpp" #include "common/NetworkManager.hpp"
#include "common/NetworkRequest.hpp"
#include "common/NetworkResult.hpp"
#include "common/Outcome.hpp"
#include "common/QLogging.hpp"
#include "providers/twitch/api/Helix.hpp"
#include "singletons/Settings.hpp" #include "singletons/Settings.hpp"
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <QApplication> #include <QApplication>
#include <QJsonArray> #include <QJsonArray>
#include <QLoggingCategory>
#include <QtConcurrent> #include <QtConcurrent>
#include <QTimer> #include <QTimer>
@ -25,17 +21,23 @@ int main(int argc, char **argv)
#ifdef SUPPORT_QT_NETWORK_TESTS #ifdef SUPPORT_QT_NETWORK_TESTS
QApplication app(argc, argv); QApplication app(argc, argv);
// make sure to always debug-log
QLoggingCategory::setFilterRules("*.debug=true");
chatterino::NetworkManager::init(); chatterino::NetworkManager::init();
// Ensure settings are initialized before any tests are run // Ensure settings are initialized before any tests are run
chatterino::Settings settings("/tmp/c2-empty-test"); QTemporaryDir settingsDir;
settingsDir.setAutoRemove(false); // we'll remove it manually
qDebug() << "Settings directory:" << settingsDir.path();
chatterino::Settings settings(settingsDir.path());
QtConcurrent::run([&app] { QtConcurrent::run([&app, &settingsDir]() mutable {
auto res = RUN_ALL_TESTS(); auto res = RUN_ALL_TESTS();
chatterino::NetworkManager::deinit(); chatterino::NetworkManager::deinit();
settingsDir.remove();
app.exit(res); app.exit(res);
}); });