diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 21a2f4092..3aadc3808 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,7 +10,11 @@ permissions: jobs: linux-glibc: runs-on: ubuntu-latest - + services: + ircd: + image: linuxserver/ngircd + ports: + - 6667:6667 steps: - uses: actions/checkout@v6 @@ -28,7 +32,7 @@ jobs: cache-to: type=gha,mode=max - name: Run tests - run: docker run -tt spectrum:tests + run: docker run -tt --network host spectrum:tests linux-clang: runs-on: ubuntu-latest diff --git a/backends/libcommuni/ircnetworkplugin.cpp b/backends/libcommuni/ircnetworkplugin.cpp index 33ed5e451..720aa0038 100644 --- a/backends/libcommuni/ircnetworkplugin.cpp +++ b/backends/libcommuni/ircnetworkplugin.cpp @@ -129,9 +129,15 @@ MyIrcSession *IRCNetworkPlugin::createSession(const std::string &user, const std void IRCNetworkPlugin::handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password, const std::map &settings) { if (!m_servers.empty()) { // legacy name is user's nickname - if (m_sessions[user] != NULL) { - LOG4CXX_WARN(logger, user << ": Already logged in."); - return; + MyIrcSession *oldSession = m_sessions[user]; + if (oldSession != NULL) { + if (oldSession->isConnected()) { + LOG4CXX_WARN(logger, user << ": Already logged in."); + return; + } + LOG4CXX_INFO(logger, user << ": Reconnecting after previous disconnect."); + oldSession->deleteLater(); + m_sessions.erase(user); } m_sessions[user] = createSession(user, m_servers[m_currentServer], legacyName, password, ""); diff --git a/backends/libcommuni/session.h b/backends/libcommuni/session.h index 55a1ee869..1d5c2cea8 100644 --- a/backends/libcommuni/session.h +++ b/backends/libcommuni/session.h @@ -81,6 +81,8 @@ class MyIrcSession : public IrcConnection void on_whoisMessageReceived(IrcMessage *message); void on_namesMessageReceived(IrcMessage *message); + bool isConnected() const { return m_connected; } + int rooms; protected Q_SLOTS: diff --git a/libtransport/AdminInterface.cpp b/libtransport/AdminInterface.cpp index f3e8f1e91..d064f0966 100644 --- a/libtransport/AdminInterface.cpp +++ b/libtransport/AdminInterface.cpp @@ -30,8 +30,10 @@ #include "transport/Frontend.h" #include "transport/MemoryUsage.h" #include "transport/Config.h" +#include "Swiften/Elements/MUCPayload.h" #include +#include #include namespace Transport { @@ -49,6 +51,178 @@ static std::string getArg(const std::string &body) { } #endif +class ConnectUserCommand : public AdminInterfaceCommand { + public: + ConnectUserCommand(Component *component, UserManager *userManager) : + AdminInterfaceCommand("connect_user", + AdminInterfaceCommand::Users, AdminInterfaceCommand::GlobalContext, + AdminInterfaceCommand::AdminMode, AdminInterfaceCommand::Execute) { + m_component = component; + m_userManager = userManager; + setDescription("Connect a user to the legacy network (simulates XMPP login)"); + addArg("user", "Bare JID of the user to connect", "string", "user@localhost"); + } + + virtual std::string handleExecuteRequest(UserInfo &uinfo, User *user, std::vector &args) { + if (args.size() < 1) return "Usage: connect_user "; + Swift::JID jid(args[0]); + if (!jid.isValid() || jid.getNode().empty()) return "Invalid JID: " + args[0]; + m_userManager->connectUser(jid); + return "OK"; + } + private: + Component *m_component; + UserManager *m_userManager; +}; + +class DisconnectUserCommand : public AdminInterfaceCommand { + public: + DisconnectUserCommand(Component *component, UserManager *userManager) : + AdminInterfaceCommand("disconnect_user", + AdminInterfaceCommand::Users, AdminInterfaceCommand::GlobalContext, + AdminInterfaceCommand::AdminMode, AdminInterfaceCommand::Execute) { + m_component = component; + m_userManager = userManager; + setDescription("Disconnect a user from the legacy network"); + addArg("user", "Bare JID of the user to disconnect", "string", "user@localhost"); + } + + virtual std::string handleExecuteRequest(UserInfo &uinfo, User *user, std::vector &args) { + if (args.size() < 1) return "Usage: disconnect_user "; + Swift::JID jid(args[0]); + if (!jid.isValid() || jid.getNode().empty()) return "Invalid JID: " + args[0]; + m_userManager->disconnectUser(jid); + return "OK"; + } + private: + Component *m_component; + UserManager *m_userManager; +}; + +class SendMessageCommand : public AdminInterfaceCommand { + public: + SendMessageCommand(Component *component, UserManager *userManager) : + AdminInterfaceCommand("send_message", + AdminInterfaceCommand::Messages, AdminInterfaceCommand::GlobalContext, + AdminInterfaceCommand::AdminMode, AdminInterfaceCommand::Execute) { + m_component = component; + m_userManager = userManager; + setDescription("Send a message on behalf of a user"); + addArg("user", "Bare JID of the sender", "string", "user@localhost"); + addArg("to", "JID to send the message to", "string", "room@conference.localhost/nick"); + addArg("body", "Message body", "string", "Hello world"); + } + + virtual std::string handleExecuteRequest(UserInfo &uinfo, User *user, std::vector &args) { + if (args.size() < 3) return "Usage: send_message "; + User *u = m_userManager->getUser(args[0]); + if (!u) return "User not online: " + args[0]; + std::shared_ptr msg(new Swift::Message()); + msg->setFrom(Swift::JID(args[0])); + msg->setTo(Swift::JID(args[1])); + msg->setBody(args[2]); + m_component->getFrontend()->onMessageReceived(msg); + return "OK"; + } + private: + Component *m_component; + UserManager *m_userManager; +}; + +class SendRoomMessageCommand : public AdminInterfaceCommand { + public: + SendRoomMessageCommand(Component *component, UserManager *userManager) : + AdminInterfaceCommand("send_room_message", + AdminInterfaceCommand::Messages, AdminInterfaceCommand::GlobalContext, + AdminInterfaceCommand::AdminMode, AdminInterfaceCommand::Execute) { + m_component = component; + m_userManager = userManager; + setDescription("Send a groupchat message to a MUC room"); + addArg("user", "Bare JID of sender", "string", "user@localhost"); + addArg("room", "Room JID", "string", "#channel@localhost"); + addArg("body", "Message body", "string", "Hello"); + } + + virtual std::string handleExecuteRequest(UserInfo &uinfo, User *user, std::vector &args) { + if (args.size() < 3) return "Usage: send_room_message "; + User *u = m_userManager->getUser(args[0]); + if (!u) return "User not online: " + args[0]; + std::shared_ptr msg(new Swift::Message()); + msg->setFrom(Swift::JID(args[0])); + msg->setTo(Swift::JID(args[1])); + msg->setBody(args[2]); + msg->setType(Swift::Message::Groupchat); + m_component->getFrontend()->onMessageReceived(msg); + return "OK"; + } + private: + Component *m_component; + UserManager *m_userManager; +}; + + +class JoinRoomCommand : public AdminInterfaceCommand { + public: + JoinRoomCommand(Component *component, UserManager *userManager) : + AdminInterfaceCommand("join_room", + AdminInterfaceCommand::Users, AdminInterfaceCommand::GlobalContext, + AdminInterfaceCommand::AdminMode, AdminInterfaceCommand::Execute) { + m_component = component; + m_userManager = userManager; + setDescription("Join a MUC room"); + addArg("user", "Bare JID of user", "string", "user@localhost"); + addArg("room", "Room JID", "string", "#channel@localhost"); + addArg("nick", "Nickname in room", "string", "user"); + } + + virtual std::string handleExecuteRequest(UserInfo &uinfo, User *user, std::vector &args) { + if (args.size() < 3) return "Usage: join_room "; + User *u = m_userManager->getUser(args[0]); + if (!u) return "Error: User not online: " + args[0]; + Swift::Presence::ref p = Swift::Presence::create(); + p->setFrom(Swift::JID(args[0] + "/" + args[2])); + p->setTo(Swift::JID(args[1] + "/" + args[2])); + p->setType(Swift::Presence::Available); + std::shared_ptr muc(new Swift::MUCPayload()); + p->addPayload(muc); + m_component->getFrontend()->onPresenceReceived(p); + return "OK"; + } + private: + Component *m_component; + UserManager *m_userManager; +}; + +class LeaveRoomCommand : public AdminInterfaceCommand { + public: + LeaveRoomCommand(Component *component, UserManager *userManager) : + AdminInterfaceCommand("leave_room", + AdminInterfaceCommand::Users, AdminInterfaceCommand::GlobalContext, + AdminInterfaceCommand::AdminMode, AdminInterfaceCommand::Execute) { + m_component = component; + m_userManager = userManager; + setDescription("Leave a MUC room"); + addArg("user", "Bare JID of user", "string", "user@localhost"); + addArg("room", "Room JID", "string", "#channel@localhost"); + addArg("nick", "Nickname in room", "string", "user"); + } + + virtual std::string handleExecuteRequest(UserInfo &uinfo, User *user, std::vector &args) { + if (args.size() < 3) return "Usage: leave_room "; + User *u = m_userManager->getUser(args[0]); + if (!u) return "Error: User not online: " + args[0]; + Swift::Presence::ref presence = Swift::Presence::create(); + presence->setFrom(Swift::JID(args[0] + "/" + args[2])); + presence->setTo(Swift::JID(args[1] + "/" + args[2])); + presence->setType(Swift::Presence::Unavailable); + m_component->getFrontend()->onPresenceReceived(presence); + return "OK"; + } + private: + Component *m_component; + UserManager *m_userManager; +}; + class StatusCommand : public AdminInterfaceCommand { public: @@ -1112,6 +1286,12 @@ AdminInterface::AdminInterface(Component *component, UserManager *userManager, N addCommand(new MessagesToXMPPCommand(m_userManager)); addCommand(new SetOAuth2CodeCommand(m_component)); addCommand(new GetOAuth2URLCommand(m_component)); + addCommand(new ConnectUserCommand(m_component, m_userManager)); + addCommand(new JoinRoomCommand(m_component, m_userManager)); + addCommand(new LeaveRoomCommand(m_component, m_userManager)); + addCommand(new DisconnectUserCommand(m_component, m_userManager)); + addCommand(new SendMessageCommand(m_component, m_userManager)); + addCommand(new SendRoomMessageCommand(m_component, m_userManager)); addCommand(new HelpCommand(&m_commands)); addCommand(new ArgsCommand(&m_commands)); addCommand(new CommandsCommand(&m_commands)); diff --git a/spectrum/src/CMakeLists.txt b/spectrum/src/CMakeLists.txt index 4d22ab206..e0f9176c6 100644 --- a/spectrum/src/CMakeLists.txt +++ b/spectrum/src/CMakeLists.txt @@ -16,7 +16,7 @@ if (ENABLE_PURPLE AND PURPLE_FOUND) add_dependencies(spectrum2 spectrum2_libpurple_backend) endif() -target_link_libraries(spectrum2 transport spectrum2-xmpp-frontend spectrum2-slack-frontend ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES} ${PROTOBUF_LIBRARY}) +target_link_libraries(spectrum2 transport spectrum2-xmpp-frontend ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES} ${PROTOBUF_LIBRARY}) target_compile_features(spectrum2 PUBLIC cxx_std_11) if(NOT MSVC AND NOT APPLE) diff --git a/spectrum/src/frontends/slack/SlackUserManager.cpp b/spectrum/src/frontends/slack/SlackUserManager.cpp index c822a3b2b..7b155ef08 100644 --- a/spectrum/src/frontends/slack/SlackUserManager.cpp +++ b/spectrum/src/frontends/slack/SlackUserManager.cpp @@ -78,7 +78,7 @@ class ListRoomsCommand : public AdminInterfaceCommand { class JoinRoomCommand : public AdminInterfaceCommand { public: - JoinRoomCommand(StorageBackend *storageBackend, Config *cfg) : AdminInterfaceCommand("join_room", + JoinRoomCommand(StorageBackend *storageBackend, Config *cfg) : AdminInterfaceCommand("join_slack_room", AdminInterfaceCommand::Frontend, AdminInterfaceCommand::UserContext, AdminInterfaceCommand::UserMode, @@ -140,7 +140,7 @@ class JoinRoomCommand : public AdminInterfaceCommand { class LeaveRoomCommand : public AdminInterfaceCommand { public: - LeaveRoomCommand(StorageBackend *storageBackend) : AdminInterfaceCommand("leave_room", + LeaveRoomCommand(StorageBackend *storageBackend) : AdminInterfaceCommand("leave_slack_room", AdminInterfaceCommand::Frontend, AdminInterfaceCommand::UserContext, AdminInterfaceCommand::UserMode, diff --git a/spectrum/src/main.cpp b/spectrum/src/main.cpp index c7934a590..584c5b51e 100644 --- a/spectrum/src/main.cpp +++ b/spectrum/src/main.cpp @@ -14,7 +14,6 @@ #include "transport/Logging.h" #include "transport/Frontend.h" #include "frontends/xmpp/XMPPFrontendPlugin.h" -#include "frontends/slack/SlackFrontendPlugin.h" #include "Swiften/EventLoop/SimpleEventLoop.h" #include "Swiften/Network/BoostNetworkFactories.h" #include diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 32ef0c2ed..f7cf337be 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,17 +1,17 @@ -add_subdirectory(libtransport) + add_subdirectory(libtransport) + add_subdirectory(integration) -if(ENABLE_TESTS) + if(ENABLE_TESTS) - add_custom_target(test - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/libtransport/libtransport_test - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests_output - ) + add_custom_target(test + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/libtransport/libtransport_test + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests_output + ) - add_custom_target(extended_test - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/libtransport/libtransport_test - COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/start.py - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests_output - ) - -endif() + add_custom_target(extended_test + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/libtransport/libtransport_test + COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/integration/run.sh ../libcommuni/irc_test.cfg ${CMAKE_CURRENT_BINARY_DIR}/integration/integration_test + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests_output + ) + endif() diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt new file mode 100644 index 000000000..b3aab5758 --- /dev/null +++ b/tests/integration/CMakeLists.txt @@ -0,0 +1,15 @@ +if(ENABLE_TESTS) + add_executable(integration_test + test_chat.cpp + ) + + target_link_libraries(integration_test + transport + ${CPPUNIT_LIBRARY} + ${Boost_LIBRARIES} + ) + + set_target_properties(integration_test PROPERTIES + COMPILE_DEFINITIONS "LIBTRANSPORT_TEST=1" + ) +endif() diff --git a/tests/integration/run.sh b/tests/integration/run.sh new file mode 100644 index 000000000..70579010e --- /dev/null +++ b/tests/integration/run.sh @@ -0,0 +1,42 @@ +#!/bin/bash +set -e +SPECTRUM="../../spectrum/src/spectrum2" +CONFIG="$1" +TEST_BIN="$2" +if [ -z "$CONFIG" ] || [ -z "$TEST_BIN" ]; then + echo "Usage: $0 " + exit 1 +fi +echo "Starting spectrum2 with config: $CONFIG" +$SPECTRUM -n "./$CONFIG" > spectrum2.log 2>&1 & +SPECTRUM_PID=$! +echo "Waiting for spectrum2 frontend..." +for i in $(seq 1 15); do + if python3 -c "import socket; s=socket.socket(); s.settimeout(1); s.connect(('127.0.0.1',5223)); s.close()" 2>/dev/null; then + echo "Spectrum2 frontend is listening"; break + fi + if [ $i -eq 15 ]; then + echo "Timeout"; cat spectrum2.log; kill $SPECTRUM_PID 2>/dev/null || true; exit 1 + fi + sleep 1 +done +# Wait for backend portfile (spectrum2 auto-spawns backend) +sleep 3 +PORTFILE="localhost.port" +if [ -f "$PORTFILE" ]; then + BACKEND_PORT=$(cat "$PORTFILE") + echo "Backend port: $BACKEND_PORT" +else + echo "Portfile $PORTFILE not found" + kill $SPECTRUM_PID 2>/dev/null || true; exit 1 +fi +echo "Running integration tests..." +EXIT_CODE=0 +"$TEST_BIN" "127.0.0.1" "$BACKEND_PORT" || EXIT_CODE=$? +kill $SPECTRUM_PID 2>/dev/null || true +wait $SPECTRUM_PID 2>/dev/null || true +if [ $EXIT_CODE -ne 0 ]; then + echo "--- spectrum2 log ---"; cat spectrum2.log +fi +echo "Integration tests completed with exit code $EXIT_CODE" +exit $EXIT_CODE diff --git a/tests/integration/test_chat.cpp b/tests/integration/test_chat.cpp new file mode 100644 index 000000000..9b3e93260 --- /dev/null +++ b/tests/integration/test_chat.cpp @@ -0,0 +1,414 @@ +/** + * Integration test for chat API (AdminInterface protobuf commands). + * + * Assumes spectrum2 already running. Connects via TCP to backend port, + * sends TYPE_QUERY commands, verifies responses. + * + * Build: links libtransport (for protocol.pb.h, WrapperMessage, BackendConfig) + */ + +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#include +#endif + +#include "transport/protocol.pb.h" +using namespace pbnetwork; + +#ifndef _WIN32 + +// Simple IRC client for message receive verification on ngircd +struct IrcClient { + int fd = -1; + std::string nick; + std::string receivedMsg; // body of last PRIVMSG received + + void connect(const std::string &host, int port, const std::string &nickname) { + nick = nickname; + fd = ::socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { perror("irc socket"); return; } + struct sockaddr_in addr; + std::memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + inet_pton(AF_INET, host.c_str(), &addr.sin_addr); + if (::connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("irc connect"); close(fd); fd = -1; return; + } + send_cmd("NICK " + nick); + send_cmd("USER " + nick + " 0 * :" + nick); + std::cerr << "[IRC] Connected " << nick << " to " << host << ":" << port << std::endl; + } + + void send_cmd(const std::string &cmd) { + std::string line = cmd + "\r\n"; + ::send(fd, line.c_str(), line.size(), MSG_NOSIGNAL); + } + + void join(const std::string &channel) { + send_cmd("JOIN " + channel); + std::cerr << "[IRC] Joining " << channel << std::endl; + } + + // Poll for incoming data, parse PRIVMSG, return true if message received + bool poll(int timeout_ms = 200) { + fd_set rfds; FD_ZERO(&rfds); FD_SET(fd, &rfds); + struct timeval tv; tv.tv_sec = 0; tv.tv_usec = timeout_ms * 1000; + if (select(fd + 1, &rfds, NULL, NULL, &tv) <= 0) return false; + char buf[4096]; + ssize_t n = recv(fd, buf, sizeof(buf) - 1, 0); + if (n <= 0) return false; + buf[n] = '\0'; + std::cerr << "[IRC] RECV: " << buf; + std::string data(buf); + // Respond to PING + if (data.find("PING ") == 0) { + std::string pong = "PONG " + data.substr(5); + ::send(fd, pong.c_str(), pong.size(), MSG_NOSIGNAL); + } + // Parse PRIVMSG + size_t pos = data.find("PRIVMSG"); + if (pos != std::string::npos) { + size_t colon = data.find(":", pos); + if (colon != std::string::npos) { + receivedMsg = data.substr(colon + 1); + while (!receivedMsg.empty() && (receivedMsg.back() == '\r' || receivedMsg.back() == '\n')) + receivedMsg.pop_back(); + return true; + } + } + return false; + } + + // Drain all pending data, return true if nick JOIN was seen + bool drainAndWatch(const std::string &watchNick) { + fd_set rfds; FD_ZERO(&rfds); FD_SET(fd, &rfds); + struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 0; + bool sawJoin = false; + while (select(fd + 1, &rfds, NULL, NULL, &tv) > 0) { + char buf[4096]; + ssize_t n = recv(fd, buf, sizeof(buf) - 1, 0); + if (n <= 0) break; + buf[n] = '\0'; + std::cerr << "[IRC] DRAIN: " << buf; + std::string data(buf); + if (data.find("PING ") == 0) { + std::string pong = "PONG " + data.substr(5); + ::send(fd, pong.c_str(), pong.size(), MSG_NOSIGNAL); + } + if (data.find("JOIN ") != std::string::npos && data.find(watchNick) != std::string::npos) { + sawJoin = true; + } + FD_ZERO(&rfds); FD_SET(fd, &rfds); + tv.tv_sec = 0; tv.tv_usec = 0; + } + return sawJoin; + } + + bool isReady() const { return fd >= 0; } + + ~IrcClient() { if (fd >= 0) close(fd); } +}; + +static int connect_tcp(const std::string &host, int port) { + int fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { perror("socket"); return -1; } + struct sockaddr_in addr; + std::memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + inet_pton(AF_INET, host.c_str(), &addr.sin_addr); + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("connect"); close(fd); return -1; + } + return fd; +} + +static bool send_wrapper(int fd, const WrapperMessage &msg) { + std::string wire; + if (!msg.SerializeToString(&wire)) return false; + uint32_t size = htonl(wire.size()); + if (send(fd, &size, 4, MSG_NOSIGNAL) != 4) return false; + if (send(fd, wire.data(), wire.size(), MSG_NOSIGNAL) != (ssize_t)wire.size()) return false; + return true; +} + +static bool recv_wrapper(int fd, WrapperMessage &msg, int timeout_sec = 5) { + // Wait for data + fd_set rfds; FD_ZERO(&rfds); FD_SET(fd, &rfds); + struct timeval tv; tv.tv_sec = timeout_sec; tv.tv_usec = 0; + if (select(fd + 1, &rfds, NULL, NULL, &tv) <= 0) return false; + + uint32_t size = 0; + if (recv(fd, &size, 4, 0) != 4) return false; + size = ntohl(size); + if (size > 65536) return false; + + std::vector buf(size); + ssize_t total = 0; + while (total < (ssize_t)size) { + ssize_t n = recv(fd, buf.data() + total, size - total, 0); + if (n <= 0) return false; + total += n; + } + std::string raw(buf.data(), size); + return msg.ParseFromString(raw); +} + +static std::string send_command(int fd, const std::string &cmd) { + BackendConfig cfg; + cfg.set_config(cmd); + + WrapperMessage wrap; + wrap.set_type(WrapperMessage_Type_TYPE_QUERY); + wrap.set_payload(cfg.SerializeAsString()); + + if (!send_wrapper(fd, wrap)) return "ERROR: send failed"; + + // Drain messages until we get TYPE_QUERY response. + // Async messages (LOGIN, STATUS_CHANGED, etc.) may arrive first. + for (int i = 0; i < 20; i++) { + WrapperMessage resp; + if (!recv_wrapper(fd, resp, 5)) return "ERROR: timeout waiting for response"; + + if (resp.type() == WrapperMessage_Type_TYPE_QUERY) { + BackendConfig resp_cfg; + if (!resp_cfg.ParseFromString(resp.payload())) + return "ERROR: parse failed"; + return resp_cfg.config(); + } + // Skip intermediate async messages (LOGIN=3, STATUS_CHANGED=17, etc.) + } + return "ERROR: too many intermediate messages"; +} + +static bool handshake(int fd) { + // Spectrum2 sends PING after connect; respond with PONG + WrapperMessage msg; + if (!recv_wrapper(fd, msg, 10)) return false; + if (msg.type() != WrapperMessage_Type_TYPE_PING) return false; + + WrapperMessage pong; + pong.set_type(WrapperMessage_Type_TYPE_PONG); + return send_wrapper(fd, pong); +} + +static int tests_failed = 0; + +static void check(const std::string &name, const std::string &actual, const std::string &expected = "OK") { + if (actual == expected) { + std::cout << name << ": PASSED" << std::endl; + } else { + std::cout << name << ": FAILED (expected='" << expected << "' actual='" << actual << "')" << std::endl; + tests_failed++; + } +} +static void check(const std::string &name, bool condition) { + if (condition) { + std::cout << name << ": PASSED" << std::endl; + } else { + std::cout << name << ": FAILED" << std::endl; + tests_failed++; + } +} + +static std::string cmd(int fd, const std::string &c) { + return send_command(fd, c); +} + +// Shared helpers — client side uses AdminInterface +static void client_setup(int fd) { + cmd(fd, "connect_user client@localhost"); sleep(2); + cmd(fd, "join_room client@localhost #channel%localhost@localhost client"); +} +static void client_teardown(int fd) { + cmd(fd, "leave_room client@localhost #channel%localhost@localhost client"); + cmd(fd, "disconnect_user client@localhost"); +} + +// Responder connects via IRC to ngircd — joins channel, receives messages +static IrcClient* irc_setup(const std::string &nick) { + IrcClient *irc = new IrcClient(); + irc->connect("127.0.0.1", 6667, nick); + if (!irc->isReady()) return irc; + irc->join("#channel"); + // Drain MOTD and wait for JOIN confirmation + for (int i = 0; i < 50; i++) { + char buf[4096]; + fd_set rfds; FD_ZERO(&rfds); FD_SET(irc->fd, &rfds); + struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 200000; + if (select(irc->fd + 1, &rfds, NULL, NULL, &tv) <= 0) continue; + ssize_t n = recv(irc->fd, buf, sizeof(buf) - 1, 0); + if (n <= 0) break; + buf[n] = '\0'; + std::string data(buf); + std::cerr << "[IRC] RECV: " << data; + // Respond to PING + if (data.find("PING ") == 0) { + std::string pong = "PONG " + data.substr(5); + ::send(irc->fd, pong.c_str(), pong.size(), MSG_NOSIGNAL); + } + if (data.find(" JOIN ") != std::string::npos && data.find(nick) != std::string::npos) { + std::cerr << "[IRC] Join confirmed for " << nick << std::endl; + break; + } + } + return irc; +} +static void irc_teardown(IrcClient *irc) { + delete irc; +} + +static void test_muc_join_leave(int fd) { + check("connect client", cmd(fd, "connect_user client@localhost"), "OK"); + sleep(2); + check("connect responder", cmd(fd, "connect_user responder@localhost"), "OK"); + sleep(2); + check("responder join", cmd(fd, "join_room responder@localhost #channel%localhost@localhost responder"), "OK"); + check("client join", cmd(fd, "join_room client@localhost #channel%localhost@localhost client"), "OK"); + check("client leave", cmd(fd, "leave_room client@localhost #channel%localhost@localhost client"), "OK"); + check("responder leave", cmd(fd, "leave_room responder@localhost #channel%localhost@localhost responder"), "OK"); + cmd(fd, "disconnect_user client@localhost"); + cmd(fd, "disconnect_user responder@localhost"); +} + +static void test_muc_echo(int fd) { + std::cout << " backends: " << cmd(fd, "backends_count") << std::endl; + + // Connect responder to IRC first so we can watch for client joining + IrcClient *irc = irc_setup("responder"); + check("irc responder connected", irc->isReady()); + + // Now connect client via AdminInterface — backend should connect to IRC as 'client' + client_setup(fd); + std::cout << " backends after connect_user: " << cmd(fd, "backends_count") << std::endl; + std::cout << " user online: " << cmd(fd, "has_online_user client@localhost") << std::endl; + + // Watch for 'client' joining #channel on IRC (proof backend connected) + bool clientJoined = false; + for (int i = 0; i < 30; i++) { + usleep(200000); + if (irc->drainAndWatch("client")) { clientJoined = true; break; } + } + std::cout << " client " << (clientJoined ? "joined IRC" : "NOT on IRC") << std::endl; + + check("send_room_message", cmd(fd, "send_room_message client@localhost #channel%localhost@localhost abc"), "OK"); + + bool got = false; + for (int i = 0; i < 50; i++) { + if (irc->poll(200)) { got = true; break; } + } + check("echo received on IRC", got); + if (got) std::cout << " IRC msg: '" << irc->receivedMsg << "'" << std::endl; + + irc_teardown(irc); + client_teardown(fd); +} + +static void test_muc_pm(int fd) { + client_setup(fd); + + IrcClient *irc = irc_setup("responder"); + check("irc responder connected", irc->isReady()); + + check("send_message", cmd(fd, "send_message client@localhost #channel%localhost@localhost/responder hello_pm"), "OK"); + + bool got = false; + for (int i = 0; i < 50; i++) { + if (irc->poll(200)) { got = true; break; } + } + check("pm received on IRC", got); + if (got) std::cout << " IRC msg: '" << irc->receivedMsg << "'" << std::endl; + + irc_teardown(irc); + client_teardown(fd); +} + +static void test_muc_away(int fd) { + client_setup(fd); + check("send away status", cmd(fd, "send_message client@localhost #channel%localhost@localhost/responder /away"), "OK"); + client_teardown(fd); +} + +static void test_muc_topic(int fd) { + client_setup(fd); + + IrcClient *irc = irc_setup("observer"); + check("irc observer connected", irc->isReady()); + + check("send to room", cmd(fd, "send_room_message client@localhost #channel%localhost@localhost ready"), "OK"); + + bool got = false; + for (int i = 0; i < 50; i++) { + if (irc->poll(200)) { got = true; break; } + } + check("topic/room message received on IRC", got); + + irc_teardown(irc); + client_teardown(fd); +} + + +int main(int argc, char *argv[]) { + if (argc < 3) { + std::cerr << "Usage: test_chat " << std::endl; + return 1; + } + + std::string host = argv[1]; + int port = std::atoi(argv[2]); + + int fd = connect_tcp(host, port); + check("connect", fd >= 0); + if (fd < 0) return 1; + check("handshake", handshake(fd)); + std::string resp = cmd(fd, "status"); + check("status", !resp.empty() && resp.find("Running") != std::string::npos); + cmd(fd, "register client@localhost client password"); + cmd(fd, "register responder@localhost responder password"); + + std::cout << "\n--- muc_join_leave ---\n"; + test_muc_join_leave(fd); + + std::cout << "\n--- muc_echo ---\n"; + test_muc_echo(fd); + + std::cout << "\n--- muc_pm ---\n"; + test_muc_pm(fd); + + std::cout << "\n--- muc_away ---\n"; + test_muc_away(fd); + + std::cout << "\n--- muc_topic ---\n"; + test_muc_topic(fd); + + + close(fd); + + if (tests_failed > 0) { + std::cout << "\n" << tests_failed << " test(s) FAILED" << std::endl; + std::cout << "\n--- spectrum2.log (last 50 lines) ---\n"; + system("cat spectrum2.log 2>/dev/null || echo '(no spectrum2.log)'"); + system("tail -50 backend.log 2>/dev/null || echo '(no backend.log)'"); + return 1; + } + std::cout << "\nAll tests PASSED" << std::endl; + return 0; +} + +#else // _WIN32 + +int main() { + std::cout << "Integration tests skipped on Windows (no backend services)" << std::endl; + return 0; +} + +#endif diff --git a/tests/libcommuni/muc_away.py b/tests/libcommuni/muc_away.py deleted file mode 100644 index aac936640..000000000 --- a/tests/libcommuni/muc_away.py +++ /dev/null @@ -1,46 +0,0 @@ -import optparse -import sys -import time -import subprocess -import os - -import sleekxmpp - - -class Responder(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, room_password, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.room_password = room_password - self.nick = nick - self.finished = False - self.add_event_handler("session_start", self.start) - self.add_event_handler("muc::" + room + "::presence", self.muc_got_online) - - self.tests = {} - self.tests["online_received"] = ["libcommuni: Received away presence from 'client'", False] - - def muc_got_online(self, presence): - if presence['muc']['nick'] == "client" and presence['show'] == "away": - self.tests["online_received"][1] = True - self.finished = True - - def start(self, event): - self.plugin['xep_0045'].joinMUC(self.room, self.nick, password=self.room_password, wait=True) - -class Client(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.add_event_handler("session_start", self.start) - self.finished = False - - self.tests = {} - - def start(self, event): - self.getRoster() - self.sendPresence() - self.plugin['xep_0045'].joinMUC(self.room, self.nick, wait=True) - self.sendPresence(ptype = "away") - diff --git a/tests/libcommuni/muc_change_topic.py b/tests/libcommuni/muc_change_topic.py deleted file mode 100644 index 46b4d046f..000000000 --- a/tests/libcommuni/muc_change_topic.py +++ /dev/null @@ -1,50 +0,0 @@ -import optparse -import sys -import time -import subprocess -import os - -import sleekxmpp - - -class Responder(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, room_password, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.finished = False - self.room_password = room_password - self.add_event_handler("session_start", self.start) - self.add_event_handler("groupchat_message", self.muc_message) - - self.tests = {} - - def muc_message(self, msg): - if msg['body'] == "ready": - self.send_message(mto=self.room, mbody=None, msubject='The new subject', mtype='groupchat') - - def start(self, event): - self.plugin['xep_0045'].joinMUC(self.room, self.nick, password=self.room_password, wait=True) - -class Client(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.add_event_handler("session_start", self.start) - self.add_event_handler("groupchat_subject", self.muc_subject) - self.finished = False - - self.tests = {} - self.tests["subject_changed"] = ["libcommuni: Change topic", False] - - def muc_subject(self, msg): - if msg['subject'] == "The new subject": - self.tests["subject_changed"][1] = True - self.finished = True - - def start(self, event): - self.getRoster() - self.sendPresence() - self.plugin['xep_0045'].joinMUC(self.room, self.nick, wait=True) - self.send_message(mto=self.room, mbody="ready", mtype='groupchat') diff --git a/tests/libcommuni/muc_echo.py b/tests/libcommuni/muc_echo.py deleted file mode 100644 index 80516d25e..000000000 --- a/tests/libcommuni/muc_echo.py +++ /dev/null @@ -1,53 +0,0 @@ -import optparse -import sys -import time -import subprocess -import os - -import sleekxmpp - - -class Responder(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, room_password, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.room_password = room_password - self.finished = False - self.add_event_handler("session_start", self.start) - self.add_event_handler("groupchat_message", self.muc_message) - - self.tests = {} - - def muc_message(self, msg): - if msg['mucnick'] != self.nick: - self.send_message(mto=msg['from'].bare, - mbody="echo %s" % msg['body'], - mtype='groupchat') - - def start(self, event): - self.plugin['xep_0045'].joinMUC(self.room, self.nick, password=self.room_password, wait=True) - -class Client(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.add_event_handler("session_start", self.start) - self.add_event_handler("groupchat_message", self.muc_message) - self.finished = False - - self.tests = {} - self.tests["echo_received"] = ["libcommuni: Send and receive messages", False] - - def muc_message(self, msg): - if msg['mucnick'] != self.nick: - if msg['body'] == "echo abc" or msg['body'] == " echo abc": - self.tests["echo_received"][1] = True - self.finished = True - - def start(self, event): - self.getRoster() - self.sendPresence() - self.plugin['xep_0045'].joinMUC(self.room, self.nick, wait=True) - self.send_message(mto=self.room, mbody="abc", mtype='groupchat') diff --git a/tests/libcommuni/muc_join_leave.py b/tests/libcommuni/muc_join_leave.py deleted file mode 100644 index 6bbf73f71..000000000 --- a/tests/libcommuni/muc_join_leave.py +++ /dev/null @@ -1,57 +0,0 @@ -import optparse -import sys -import time -import subprocess -import os - -import sleekxmpp - - -class Responder(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, room_password, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.room_password = room_password - self.nick = nick - self.finished = False - self.add_event_handler("session_start", self.start) - self.add_event_handler("muc::" + room + "::got_online", self.muc_got_online) - self.add_event_handler("muc::" + room + "::got_offline", self.muc_got_offline) - - self.tests = {} - self.tests["online_received"] = ["libcommuni: Received available presence from 'client'", False] - self.tests["offline_received"] = ["libcommuni: Received unavailable presence frin 'client'", False] - - def muc_got_online(self, presence): - if presence['muc']['nick'] == "client": - self.tests["online_received"][1] = True - - def muc_got_offline(self, presence): - if presence['muc']['nick'] == "client": - self.tests["offline_received"][1] = True - self.finished = True - - def start(self, event): - self.plugin['xep_0045'].joinMUC(self.room, self.nick, password=self.room_password, wait=True) - -class Client(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.add_event_handler("session_start", self.start) - self.add_event_handler("muc::" + room + "::got_online", self.muc_got_online) - self.finished = False - - self.tests = {} - self.tests["online_received"] = ["libcommuni: Received available presence from 'responder'", False] - - def muc_got_online(self, presence): - if presence['muc']['nick'] == "responder": - self.tests["online_received"][1] = True - self.plugin['xep_0045'].leaveMUC(self.room, self.nick) - - def start(self, event): - self.getRoster() - self.sendPresence() - self.plugin['xep_0045'].joinMUC(self.room, self.nick, wait=True) diff --git a/tests/libcommuni/muc_join_nickname_used.py b/tests/libcommuni/muc_join_nickname_used.py deleted file mode 100644 index 037d98bc6..000000000 --- a/tests/libcommuni/muc_join_nickname_used.py +++ /dev/null @@ -1,57 +0,0 @@ -import optparse -import sys -import time -import subprocess -import os - -import sleekxmpp - - -class Responder(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, room_password, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.room_password = room_password - self.nick = nick - self.finished = False - self.add_event_handler("session_start", self.start) - self.add_event_handler("muc::" + room + "::got_online", self.muc_got_online) - self.add_event_handler("muc::" + room + "::got_offline", self.muc_got_offline) - - self.tests = {} - self.tests["online_received"] = ["libcommuni: Received available presence", False] - self.tests["offline_received"] = ["libcommuni: Received unavailable presence", False] - - def muc_got_online(self, presence): - if presence['muc']['nick'] == "respond_": - self.tests["online_received"][1] = True - self.send_message(mto=self.room, - mbody="disconnect please :)", - mtype='groupchat') - - def muc_got_offline(self, presence): - if presence['muc']['nick'] == "respond_": - self.tests["offline_received"][1] = True - self.finished = True - - def start(self, event): - self.plugin['xep_0045'].joinMUC(self.room, "respond", password=self.room_password, wait=True) - -class Client(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.add_event_handler("session_start", self.start) - self.add_event_handler("groupchat_message", self.muc_message) - self.finished = False - - self.tests = {} - - def muc_message(self, msg): - self.plugin['xep_0045'].leaveMUC(self.room, "respond_") - - def start(self, event): - self.getRoster() - self.sendPresence() - self.plugin['xep_0045'].joinMUC(self.room, "respond", wait=True) diff --git a/tests/libcommuni/muc_pm.py b/tests/libcommuni/muc_pm.py deleted file mode 100644 index 8874e91aa..000000000 --- a/tests/libcommuni/muc_pm.py +++ /dev/null @@ -1,62 +0,0 @@ -import optparse -import sys -import time -import subprocess -import os - -import sleekxmpp - - -class Responder(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, room_password, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.room_password = room_password - self.nick = nick - self.finished = False - self.add_event_handler("session_start", self.start) - self.add_event_handler("message", self.message) - - self.tests = {} - - def message(self, msg): - if msg['body'] == "abc" and msg['from'] == self.room + "/client": - self.send_message(mto=self.room + "/client", - mbody="echo %s" % msg['body'], - mtype='chat') - elif msg['body'] == "def" and msg['from'] == self.room + "/client": - self.send_message(mto=self.room + "/client", - mbody="echo %s" % msg['body'], - mtype='chat') - else: - self.finished = True - - def start(self, event): - self.plugin['xep_0045'].joinMUC(self.room, self.nick, password=self.room_password, wait=True) - -class Client(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.add_event_handler("session_start", self.start) - self.add_event_handler("message", self.message) - self.finished = False - - self.tests = {} - self.tests["echo1_received"] = ["libcommuni: Send and receive private messages - 1st msg", False] - self.tests["echo2_received"] = ["libcommuni: Send and receive private messages - 2nd msg", False] - - def message(self, msg): - if msg['body'] == "echo abc" and msg['from'] == self.room + "/responder": - self.tests["echo1_received"][1] = True - self.send_message(mto=self.room + "/responder", mbody="def", mtype='chat') - elif msg['body'] == "echo def" and msg['from'] == self.room + "/responder": - self.tests["echo2_received"][1] = True - self.finished = True - - def start(self, event): - self.getRoster() - self.sendPresence() - self.plugin['xep_0045'].joinMUC(self.room, self.nick, wait=True) - self.send_message(mto=self.room + "/responder", mbody="abc", mtype='chat') diff --git a/tests/libcommuni/muc_whois.py b/tests/libcommuni/muc_whois.py deleted file mode 100644 index 39539ee16..000000000 --- a/tests/libcommuni/muc_whois.py +++ /dev/null @@ -1,49 +0,0 @@ -import optparse -import sys -import time -import subprocess -import os - -import sleekxmpp - - -class Responder(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, room_password, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.finished = False - self.room_password = room_password - self.add_event_handler("session_start", self.start) - self.tests = {} - - def start(self, event): - self.plugin['xep_0045'].joinMUC(self.room, self.nick, password=self.room_password, wait=True) - -class Client(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.add_event_handler("session_start", self.start) - self.add_event_handler("groupchat_message", self.muc_message) - self.finished = False - - self.tests = {} - self.tests["whois1_received"] = ["libcommuni: Receive /whois command response", False] - self.tests["whois2_received"] = ["libcommuni: Receive /whois command response for invalid nickname", False] - - def muc_message(self, msg): - if msg['mucnick'] != self.nick: - if msg["body"] == "responder is connected to irc.example.net (responder)\nresponder is a user on channels: @#channel": - self.tests["whois1_received"][1] = True - elif msg["body"] == "nonexisting: No such client": - self.tests["whois2_received"][1] = True - self.finished = True - - def start(self, event): - self.getRoster() - self.sendPresence() - self.plugin['xep_0045'].joinMUC(self.room, self.nick, wait=True) - self.send_message(mto=self.room, mbody="/whois responder", mtype='groupchat') - self.send_message(mto=self.room, mbody="/whois nonexisting", mtype='groupchat') diff --git a/tests/libpurple_jabber/avatar.py b/tests/libpurple_jabber/avatar.py deleted file mode 100644 index 1d69079ba..000000000 --- a/tests/libpurple_jabber/avatar.py +++ /dev/null @@ -1,54 +0,0 @@ -import optparse -import sys -import time -import subprocess -import os - -import sleekxmpp - -import struct -import zlib -def chunk(t, data): - return struct.pack('>I', len(data)) + t + data -#+ struct.pack('>I', zlib.crc32(t + data)) -png = b'\x89PNG\r\n\x1A\n' + chunk(b'IHDR', struct.pack('>IIBBBBB', 1, 1, 1, 0, 0, 0, 0)) + chunk(b'IDAT', zlib.compress(struct.pack('>BB', 0, 0))) + chunk(b'IEND', b'') - - -class Responder(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, room_password, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.room_password = room_password - self.nick = nick - self.finished = False - self.register_plugin('xep_0153') # vCard-based avatars - self.add_event_handler("session_start", self.start) - self.add_event_handler("vcard_avatar_update", self.vcard_avatar_update) - - self.tests = {} - self.tests["photohash"] = ["libpurple: Photo hash received in Presence", False] - - def vcard_avatar_update(self, presence): - self.tests["photohash"][1] = True - self.finished = True - - def start(self, event): - self.getRoster() - self.sendPresence() - -class Client(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.register_plugin('xep_0153') # vCard-based avatars - self.add_event_handler("session_start", self.start) - self.finished = False - - self.tests = {} - - def start(self, event): - self.getRoster() - self.sendPresence() - self['xep_0153'].set_avatar(avatar=png, mtype='image/png') - self.finished = True diff --git a/tests/libpurple_jabber/carbons.py b/tests/libpurple_jabber/carbons.py deleted file mode 100644 index ed963b729..000000000 --- a/tests/libpurple_jabber/carbons.py +++ /dev/null @@ -1,211 +0,0 @@ -import optparse -import sys -import time -import subprocess -import os - -import sleekxmpp -from sleekxmpp.jid import JID - -# Verifies that our own messages sent elsewhere are delivered as carbons, and that our local -# messages are not. - -# Requires XMPP backend with carbons enabled. - -# NOTE: Prpl-jabber doesn't subscribe to carbons out of the box so -# What we can test: -# - Outside client gets carbons when Spectrum client speaks -# - Spectrum ignores its own messages as they're routed to it -# What we cannot test: -# - Spectrum passes through carbons of outside client messages - -class Client(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, alt_jid, responder_jid): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.full_jid = jid - self.alt_jid = JID(alt_jid) # our own alternative jid (backend/spectrum) - self.responder_jid = JID(responder_jid) - self.register_plugin('xep_0030') # Service Discovery - self.register_plugin('xep_0199') # XMPP Ping - self.register_plugin('xep_0280') # Message Carbons - self.add_event_handler("session_start", self.start) - self.add_event_handler("message", self.message) - self.add_event_handler("carbon_sent", self.carbon_sent) - self.add_event_handler("carbon_received", self.carbon_received) - self['feature_mechanisms'].unencrypted_plain = True - self.weird_cnt = 0 - self.cs_m1_cnt = 0 - self.cs_m2_cnt = 0 - self.cs_m3_cnt = 0 - - def __str__(self): - return "Client("+str(self.jid)+")" - - def run(self, host, port): - #print str(self)+": connect("+host+":"+str(port)+")" - to = (host, port) - if self.connect(to): - self.process(block=False) - else: - raise Exception("connect() failed") - - def start(self, event): - #print str(self)+": on_start" - self.getRoster() - self.sendPresence() - #print str(self)+": enabling xep_0280..." - self.plugin['xep_0280'].enable() - #print str(self)+": xep_0280 enable()d" - time.sleep(5) # let other clients connect - - # We'll send several pairs of messages from each client: - # 1. Two unique ones - # 2. Two identical ones - # 3. Three times two unique ones - # Each client should receive 1 copy of each message sent by another client. - # Unique ones verify that it's the other message that we're getting, - # identical ones verify that we're getting it even if it's identical to the one we've sent. - - #print str(self)+": sending to " + str(self.responder_jid) - self.send_message(mto=self.responder_jid, mbody="Message 1 from "+str(self.full_jid)) - self.send_message(mto=self.responder_jid, mbody="Message 2"); - self.send_message(mto=self.responder_jid, mbody="Message 3 from "+str(self.full_jid)) - self.send_message(mto=self.responder_jid, mbody="Message 3 from "+str(self.full_jid)) - self.send_message(mto=self.responder_jid, mbody="Message 3 from "+str(self.full_jid)) - - def message(self, msg): - print(str(self)+": Weird unexpected message: "+str(msg)) - self.weird_cnt += 1 - - def carbon_sent(self, msg): - #print str(self)+": on_carbon_sent: "+str(msg) - mfrom = msg['carbon_sent']['from'] - mbody = msg['carbon_sent']['body'] - - if mfrom == self.jid: - # Carbons are delivered with from==JID/other_resource, or with Spectrum, "/bot" - print(str(self)+": Weird carbon_sent: from == own exact JID") - self.weird_cnt += 1 - return - - if (mfrom.bare != JID(self.jid).bare) and (mfrom.bare != self.alt_jid.bare): - # Carbons should be from our other resources - print(str(self)+": Weird carbon_sent: from.bare != our JID") - self.weird_cnt += 1 - return - - if (mbody.startswith("Message 1 from")): - #print str(self)+": message1 carbon!" - self.cs_m1_cnt += 1 - return - - if (mbody == "Message 2"): - #print str(self)+": message2 carbon!" - self.cs_m2_cnt += 1 - return - - if (mbody.startswith("Message 3 from")): - #print str(self)+": message3 carbon!" - self.cs_m3_cnt += 1 - return - - print(str(self)+": Weird unexpected carbon_sent: "+str(msg)) - self.weird_cnt += 1 - - def carbon_received(self, msg): - print(str(self)+": Weird unexpected carbon_received: "+str(msg)) - self.weird_cnt += 1 - - def print_stats(self): - print(str(self)+": msg1="+str(self.cs_m1_cnt)+", msg2="+str(self.cs_m2_cnt)+", msg3="+str(self.cs_m3_cnt)+", weird="+str(self.weird_cnt)) - - -class Responder(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, client1_jid, client2_jid): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.add_event_handler("session_start", self.start) - self.add_event_handler("message", self.message) - self.client1_jid = JID(client1_jid) - self.client2_jid = JID(client2_jid) - self.incoming_cnt = 0 - - def __str__(self): - return "Responder("+str(self.jid)+")" - - def run(self, host, port): - #print "Responder: connect("+host+":"+str(port)+")" - to = (host, port) - if self.connect(to): - self.process(block=False) - else: - raise Exception("connect() failed") - - def start(self, event): - #print "Responder: on_start" - self.getRoster() - self.sendPresence() - - def message(self, msg): - #print "Responder: on_message: "+str(msg) - self.incoming_cnt += 1 - - def print_stats(self): - print(str(self)+": incoming_cnt="+str(self.incoming_cnt)) - - -class TestCase(): - def __init__(self, env): - # Only run for configurations with backend_jids (legacy XMPP) - self.enabled = not ((not env.client_backend_jid) or (not env.responder_backend_jid)) - self.env = env - self.tests = {} - self.tests["no_weird"] = ["backend-libpurple: No weird carbon-related messages delivered", False] - self.tests["message1"] = ["backend-libpurple: Carbons are delivered to clients across spectrum boundary", False] - self.tests["message1_exact"] = ["backend-libpurple: No unexpected carbons are delivered", False] - self.tests["message2"] = ["backend-libpurple: Identical carbons are delivered correctly", False] - self.tests["message3"] = ["backend-libpurple: Repeated carbons are delivered correctly", False] - self.tests["responder_got_all"] = ["backend-libpurple: All messages have been delivered to responder", False] - - def setup(self): - # We need two clients: one connecting to spectrum, the other to backend. - self.client1 = Client(self.env.client_jid+"/res1", self.env.client_password, - self.env.client_backend_jid+"/res1", self.env.responder_jid) - self.client1.run(self.env.spectrum.host, self.env.spectrum.port) - self.client2 = Client(self.env.client_backend_jid+"/res2", self.env.client_backend_password, - self.env.client_jid+"/res1", self.env.responder_backend_jid+"/res2") - self.client2.run(self.env.backend.host, self.env.backend.port) - # And one buddy at backend - self.responder = Responder(self.env.responder_backend_jid, self.env.responder_backend_password, - self.client1.alt_jid, - self.client2.jid) - self.responder.run(self.env.backend.host, self.env.backend.port) - - def finished(self): - return ( - self.client1.cs_m1_cnt>=1 and self.client1.cs_m2_cnt>=1 and self.client1.cs_m3_cnt>=3 and - self.client2.cs_m1_cnt>=1 and self.client2.cs_m2_cnt>=1 and self.client2.cs_m3_cnt>=3 - and self.responder.incoming_cnt>=10 ) - - def teardown(self): - if hasattr(self, 'client1') and hasattr(self, 'client2'): - self.tests["no_weird"][1] = self.client1.weird_cnt==0 and self.client2.weird_cnt==0 - self.tests["message1"][1] = self.client1.cs_m1_cnt>=1 and self.client2.cs_m1_cnt>=1 - self.tests["message1_exact"][1] = self.client1.cs_m1_cnt==1 and self.client2.cs_m1_cnt==1 - self.tests["message2"][1] = self.client1.cs_m2_cnt==1 and self.client2.cs_m2_cnt==1 - self.tests["message3"][1] = self.client1.cs_m3_cnt==3 and self.client2.cs_m3_cnt==3 - fail = not ( self.tests["no_weird"][1] and self.tests["message1_exact"][1] - and self.tests["message2"][1] and self.tests["message3"][1] ) - if fail: - self.client1.print_stats() - self.client2.print_stats() - if hasattr(self, 'client1'): - self.client1.disconnect() - if hasattr(self, 'client2'): - self.client2.disconnect() - if hasattr(self, 'responder'): - self.tests["responder_got_all"][1] = (self.responder.incoming_cnt==10) - self.responder.disconnect() - if not self.tests["responder_got_all"][1]: - self.responder.print_stats() - self.tests = self.tests.values() # the wrapper expects value pairs - pass diff --git a/tests/libpurple_jabber/logging-backend.cfg b/tests/libpurple_jabber/logging-backend.cfg index bb058f7ef..c804a0f7f 100644 --- a/tests/libpurple_jabber/logging-backend.cfg +++ b/tests/libpurple_jabber/logging-backend.cfg @@ -1,4 +1,4 @@ log4j.rootLogger=debug, R -log4j.appender.R=org.apache.log4j.SyslogAppender +log4j.appender.R=org.apache.log4j.ConsoleAppender log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=${pid}: %d %-5p %c: %m%n diff --git a/tests/libpurple_jabber/muc_echo.py b/tests/libpurple_jabber/muc_echo.py deleted file mode 100644 index 645341631..000000000 --- a/tests/libpurple_jabber/muc_echo.py +++ /dev/null @@ -1,61 +0,0 @@ -import optparse -import sys -import time -import subprocess -import os - -import sleekxmpp - - -class Responder(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, room_password, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.room_password = room_password - self.finished = False - self.register_plugin("xep_0004") # Forms - self.register_plugin("xep_0045") # MUC - self.add_event_handler("session_start", self.start) - self.add_event_handler("groupchat_message", self.muc_message) - - self.tests = {} - - def muc_message(self, msg): - if msg['mucnick'] != self.nick: - self.send_message(mto=msg['from'].bare, - mbody="echo %s" % msg['body'], - mtype='groupchat') - - def start(self, event): - self.plugin['xep_0045'].joinMUC(self.room, self.nick, password=self.room_password, wait=True) - # Room creator must provide initial configuration or the room will be item-not-found for others - # Not supported by Spectrum though - #form = self.plugin['xep_0004'].make_form(ftype='submit') - #self.plugin['xep_0045'].setRoomConfig(self.room, form) - self.finished = True # Just reply to requests - -class Client(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.register_plugin("xep_0045") # MUC - self.add_event_handler("session_start", self.start) - self.add_event_handler("groupchat_message", self.muc_message) - self.finished = False - - self.tests = {} - self.tests["echo_received"] = ["MUC: Send and receive messages", False] - - def muc_message(self, msg): - if msg['mucnick'] != self.nick: - if msg['body'] == "echo abc" or msg['body'] == " echo abc": - self.tests["echo_received"][1] = True - self.finished = True - - def start(self, event): - self.getRoster() - self.sendPresence() - self.plugin['xep_0045'].joinMUC(self.room, self.nick, wait=True) - self.send_message(mto=self.room, mbody="abc", mtype='groupchat') diff --git a/tests/libpurple_jabber/muc_join_leave.py b/tests/libpurple_jabber/muc_join_leave.py deleted file mode 100644 index b09946b2b..000000000 --- a/tests/libpurple_jabber/muc_join_leave.py +++ /dev/null @@ -1,52 +0,0 @@ -import optparse -import sys -import time -import subprocess -import os - -import sleekxmpp - - -class Responder(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, room_password, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.room_password = room_password - self.nick = nick - self.finished = False - self.add_event_handler("session_start", self.start) - self.add_event_handler("muc::" + room + "::got_online", self.muc_got_online) - self.add_event_handler("muc::" + room + "::got_offline", self.muc_got_offline) - - self.tests = {} - self.tests["online_received"] = ["MUC: Received available presence", False] - self.tests["offline_received"] = ["MUC: Received unavailable presence", False] - - def muc_got_online(self, presence): - if presence['muc']['nick'] == "client": - self.tests["online_received"][1] = True - - def muc_got_offline(self, presence): - if presence['muc']['nick'] == "client": - self.tests["offline_received"][1] = True - self.finished = True - - def start(self, event): - self.plugin['xep_0045'].joinMUC(self.room, self.nick, password=self.room_password, wait=True) - -class Client(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.add_event_handler("session_start", self.start) - self.finished = False - - self.tests = {} - - def start(self, event): - self.getRoster() - self.sendPresence() - self.plugin['xep_0045'].joinMUC(self.room, self.nick, wait=True) - self.plugin['xep_0045'].leaveMUC(self.room, self.nick) - self.finished = True diff --git a/tests/libpurple_jabber/muc_pm.py b/tests/libpurple_jabber/muc_pm.py deleted file mode 100644 index 78f5decf8..000000000 --- a/tests/libpurple_jabber/muc_pm.py +++ /dev/null @@ -1,62 +0,0 @@ -import optparse -import sys -import time -import subprocess -import os - -import sleekxmpp - - -class Responder(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, room_password, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.room_password = room_password - self.nick = nick - self.finished = False - self.add_event_handler("session_start", self.start) - self.add_event_handler("message", self.message) - - self.tests = {} - - def message(self, msg): - if msg['body'] == "abc" and msg['from'] == self.room + "/client": - self.send_message(mto=self.room + "/client", - mbody="echo %s" % msg['body'], - mtype='chat') - elif msg['body'] == "def" and msg['from'] == self.room + "/client": - self.send_message(mto=self.room + "/client", - mbody="echo %s" % msg['body'], - mtype='chat') - else: - self.finished = True - - def start(self, event): - self.plugin['xep_0045'].joinMUC(self.room, self.nick, password=self.room_password, wait=True) - -class Client(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.add_event_handler("session_start", self.start) - self.add_event_handler("message", self.message) - self.finished = False - - self.tests = {} - self.tests["echo1_received"] = ["MUC: Send and receive private messages - 1st msg", False] - self.tests["echo2_received"] = ["MUC: Send and receive private messages - 2nd msg", False] - - def message(self, msg): - if msg['body'] == "echo abc" and msg['from'] == self.room + "/responder": - self.tests["echo1_received"][1] = True - self.send_message(mto=self.room + "/responder", mbody="def", mtype='chat') - elif msg['body'] == "echo def" and msg['from'] == self.room + "/responder": - self.tests["echo2_received"][1] = True - self.finished = True - - def start(self, event): - self.getRoster() - self.sendPresence() - self.plugin['xep_0045'].joinMUC(self.room, self.nick, wait=True) - self.send_message(mto=self.room + "/responder", mbody="abc", mtype='chat') diff --git a/tests/libpurple_jabber/muc_topic.py b/tests/libpurple_jabber/muc_topic.py deleted file mode 100644 index c498ad96d..000000000 --- a/tests/libpurple_jabber/muc_topic.py +++ /dev/null @@ -1,52 +0,0 @@ -import optparse -import sys -import time -import subprocess -import os - -import sleekxmpp - - -class Responder(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, room_password, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.room_password = room_password - self.nick = nick - self.finished = False - self.add_event_handler("session_start", self.start) - self.add_event_handler("groupchat_subject", self.message) - - self.tests = {} - self.tests["subject_set1"] = ["libpurple: Set subject", False] - - def message(self, msg): - if msg['subject'] == "New subject": - self.tests["subject_set1"][1] = True - self.finished = True - - def start(self, event): - self.plugin['xep_0045'].joinMUC(self.room, self.nick, password=self.room_password, wait=True) - self.send_message(mto=self.room, mbody=None, msubject="New subject", mtype='groupchat') - -class Client(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.add_event_handler("session_start", self.start) - self.add_event_handler("groupchat_subject", self.message) - self.finished = False - - self.tests = {} - self.tests["subject_set2"] = ["libpurple: Receive subject", False] - - def message(self, msg): - if msg['subject'] == "New subject": - self.tests["subject_set2"][1] = True - self.finished = True - - def start(self, event): - self.getRoster() - self.sendPresence() - self.plugin['xep_0045'].joinMUC(self.room, self.nick, wait=True) diff --git a/tests/libpurple_jabber/prosody.cfg.lua b/tests/libpurple_jabber/prosody.cfg.lua index ab369520b..dc2f5eb38 100644 --- a/tests/libpurple_jabber/prosody.cfg.lua +++ b/tests/libpurple_jabber/prosody.cfg.lua @@ -22,7 +22,7 @@ -- Example: admins = { "user1@example.com", "user2@example.net" } admins = { } -data_path="." +-- data_path defaults to /var/lib/prosody which has correct permissions -- Enable use of libevent for better performance under high load -- For more information see: http://prosody.im/doc/libevent diff --git a/tests/libtransport/AdminInterface.cpp b/tests/libtransport/AdminInterface.cpp index e3244ed2c..8338243f0 100644 --- a/tests/libtransport/AdminInterface.cpp +++ b/tests/libtransport/AdminInterface.cpp @@ -64,7 +64,7 @@ class AdminInterfaceTest : public CPPUNIT_NS :: TestFixture, public BasicSlackTe } void joinRoomArgs() { - std::string resp = sendAdminMessage("args join_room"); + std::string resp = sendAdminMessage("args join_slack_room"); CPPUNIT_ASSERT_EQUAL(std::string("nickname - \"Nickname in 3rd-party room\" Example: \"BotNickname\" Type: \"string\"\n" "legacy_room - \"3rd-party room name\" Example: \"3rd-party room name\" Type: \"string\"\n" "legacy_server - \"3rd-party server\" Example: \"3rd.party.server.org\" Type: \"string\"\n" @@ -87,13 +87,13 @@ class AdminInterfaceTest : public CPPUNIT_NS :: TestFixture, public BasicSlackTe std::string resp = sendAdminMessage("list_rooms user@localhost"); CPPUNIT_ASSERT_EQUAL(std::string(""), resp); - resp = sendAdminMessage("join_room user@localhost SlackBot spectrum conference.spectrum.im slack_channel"); + resp = sendAdminMessage("join_slack_room user@localhost SlackBot spectrum conference.spectrum.im slack_channel"); CPPUNIT_ASSERT_EQUAL(std::string("Joined the room"), resp); resp = sendAdminMessage("list_rooms user@localhost"); CPPUNIT_ASSERT_EQUAL(std::string("connected room SlackBot spectrum conference.spectrum.im slack_channel\n"), resp); - resp = sendAdminMessage("leave_room user@localhost slack_channel"); + resp = sendAdminMessage("leave_slack_room user@localhost slack_channel"); CPPUNIT_ASSERT_EQUAL(std::string("Left the room"), resp); resp = sendAdminMessage("list_rooms user@localhost"); @@ -103,10 +103,10 @@ class AdminInterfaceTest : public CPPUNIT_NS :: TestFixture, public BasicSlackTe void badArgCount() { addUser(); std::string resp; - resp = sendAdminMessage("join_room user@localhost SlackBot spectrum conference.spectrum.im slack_channel unknown"); + resp = sendAdminMessage("join_slack_room user@localhost SlackBot spectrum conference.spectrum.im slack_channel unknown"); CPPUNIT_ASSERT_EQUAL(std::string("Error: Too many arguments."), resp); - resp = sendAdminMessage("join_room user@localhost SlackBot spectrum conference.spectrum.im"); + resp = sendAdminMessage("join_slack_room user@localhost SlackBot spectrum conference.spectrum.im"); CPPUNIT_ASSERT_EQUAL(std::string("Error: Argument is missing."), resp); } @@ -114,7 +114,7 @@ class AdminInterfaceTest : public CPPUNIT_NS :: TestFixture, public BasicSlackTe addUser(); std::string resp; resp = sendAdminMessage("commands"); - CPPUNIT_ASSERT(resp.find("join_room - \"Join the room\" Category: Frontend AccesMode: User Context: User") != std::string::npos); + CPPUNIT_ASSERT(resp.find("join_slack_room - \"Join the room\" Category: Frontend AccesMode: User Context: User") != std::string::npos); } void variablesCommand() { diff --git a/tests/libtransport/CMakeLists.txt b/tests/libtransport/CMakeLists.txt index 0116d7457..a8996a8c3 100644 --- a/tests/libtransport/CMakeLists.txt +++ b/tests/libtransport/CMakeLists.txt @@ -1,33 +1,32 @@ -file(GLOB SRC *.cpp *.h) -file(GLOB_RECURSE SWIFTEN_SRC ../include/Swiften/*.cpp) + file(GLOB SRC *.cpp *.h) + file(GLOB_RECURSE SWIFTEN_SRC ../include/Swiften/*.cpp) -# Build without openssl on msvc -if(NOT MSVC) - if(APPLE) - string(REGEX REPLACE "[^;]+;?/Schannel/[^;]+;?" "" SWIFTEN_SRC "${SWIFTEN_SRC}") - string(REGEX REPLACE "[^;]+;?/OpenSSL/[^;]+;?" "" SWIFTEN_SRC "${SWIFTEN_SRC}") + # Build without openssl on msvc + if(NOT MSVC) + if(APPLE) + string(REGEX REPLACE "[^;]+;?/Schannel/[^;]+;?" "" SWIFTEN_SRC "${SWIFTEN_SRC}") + string(REGEX REPLACE "[^;]+;?/OpenSSL/[^;]+;?" "" SWIFTEN_SRC "${SWIFTEN_SRC}") + else() + string(REGEX REPLACE "[^;]+;?/Schannel/[^;]+;?" "" SWIFTEN_SRC "${SWIFTEN_SRC}") + string(REGEX REPLACE "[^;]+;?/SecureTransport/[^;]+;?" "" SWIFTEN_SRC "${SWIFTEN_SRC}") + endif() else() - string(REGEX REPLACE "[^;]+;?/Schannel/[^;]+;?" "" SWIFTEN_SRC "${SWIFTEN_SRC}") - string(REGEX REPLACE "[^;]+;?/SecureTransport/[^;]+;?" "" SWIFTEN_SRC "${SWIFTEN_SRC}") + string(REGEX REPLACE "[^;]+;?/OpenSSL/[^;]+;?" "" SWIFTEN_SRC "${SWIFTEN_SRC}") + string(REGEX REPLACE "[^;]+;?/SecureTransport/[^;]+;?" "" SWIFTEN_SRC "${SWIFTEN_SRC}") endif() -else() - string(REGEX REPLACE "[^;]+;?/OpenSSL/[^;]+;?" "" SWIFTEN_SRC "${SWIFTEN_SRC}") - string(REGEX REPLACE "[^;]+;?/SecureTransport/[^;]+;?" "" SWIFTEN_SRC "${SWIFTEN_SRC}") -endif() -file(GLOB HEADERS ../../include/transport/*.h) -include_directories(../../spectrum/src/frontends/xmpp/) -include_directories(../../spectrum/src/frontends/slack/) + file(GLOB HEADERS ../../include/transport/*.h) + include_directories(../../spectrum/src/frontends/xmpp/) + include_directories(../../spectrum/src/frontends/slack/) -if(CPPUNIT_FOUND) - file(GLOB SRC_TEST *.cpp) - file(GLOB SRC_TEST_FRONTEND_XMPP ../../spectrum/src/frontends/xmpp/*.cpp) - file(GLOB SRC_TEST_FRONTEND_SLACK ../../spectrum/src/frontends/slack/*.cpp) + if(CPPUNIT_FOUND) + file(GLOB SRC_TEST *.cpp) + file(GLOB SRC_TEST_FRONTEND_XMPP ../../spectrum/src/frontends/xmpp/*.cpp) + file(GLOB SRC_TEST_FRONTEND_SLACK ../../spectrum/src/frontends/slack/*.cpp) - add_executable(libtransport_test ${SRC_TEST} ${SRC_TEST_FRONTEND_XMPP} ${SRC_TEST_FRONTEND_SLACK}) - target_compile_features(libtransport_test PUBLIC cxx_std_11) - set_target_properties(libtransport_test PROPERTIES COMPILE_DEFINITIONS LIBTRANSPORT_TEST=1) - + add_executable(libtransport_test ${SRC_TEST} ${SRC_TEST_FRONTEND_XMPP} ${SRC_TEST_FRONTEND_SLACK}) + target_compile_features(libtransport_test PUBLIC cxx_std_11) + set_target_properties(libtransport_test PROPERTIES COMPILE_DEFINITIONS LIBTRANSPORT_TEST=1) - target_link_libraries(libtransport_test transport ${CPPUNIT_LIBRARY} ${Boost_LIBRARIES}) -endif() + target_link_libraries(libtransport_test transport ${CPPUNIT_LIBRARY} ${Boost_LIBRARIES}) + endif() diff --git a/tests/slack_jabber/bad_password.py b/tests/slack_jabber/bad_password.py deleted file mode 100644 index 5a6f7ee5e..000000000 --- a/tests/slack_jabber/bad_password.py +++ /dev/null @@ -1,66 +0,0 @@ -import optparse -import sys -import time -import subprocess -import os - -import sleekxmpp - - -class Responder(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, room_password, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.room_password = room_password - self.nick = nick - self.finished = False - self.add_event_handler("session_start", self.start) - self.add_event_handler("message", self.message) - - self.tests = {} - self.tests["not_authorized"] = ["'Not Authorized' received", False] - self.tests["help_received"] = ["Help received", False] - self.tests["register_received"] = ["Password changed", False] - self.tests["abc_received"] = ["Test message received", False] - - def message(self, msg): - if msg['body'] == "Not Authorized" or msg['body'] == "Server may require plaintext authentication over an unencrypted stream": - self.tests["not_authorized"][1] = True - elif msg['body'].find("try using") != -1: - self.send_message(mto="spectrum2@spectrum2tests.xmpp.slack.com", mbody=".spectrum2 register client@localhost password #spectrum2_contactlist") - self.tests["help_received"][1] = True - elif msg['body'] == "You have successfully registered 3rd-party account. Spectrum 2 is now connecting to the 3rd-party network.": - self.tests["register_received"][1] = True - elif msg['body'] == "abc": - self.tests["abc_received"][1] = True - self.finished = True - - def start(self, event): - self.plugin['xep_0045'].joinMUC(self.room, self.nick, password=self.room_password, wait=False) - -class Client(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.add_event_handler("session_start", self.start) - self.add_event_handler("message", self.message) - self.finished = False - - self.tests = {} - - def message(self, msg): - pass - #print "client", msg['body'] - #if msg['body'] == "echo abc" and msg['from'] == self.room + "/responder": - #self.tests["echo1_received"][1] = True - #self.send_message(mto=self.room + "/responder", mbody="def", mtype='chat') - #elif msg['body'] == "echo def" and msg['from'] == self.room + "/responder": - #self.tests["echo2_received"][1] = True - #self.finished = True - - def start(self, event): - self.getRoster() - self.sendPresence() - self.plugin['xep_0045'].joinMUC(self.room, self.nick, wait=True) - self.send_message(mto=self.room, mbody="abc", mtype='groupchat') diff --git a/tests/slack_jabber/muc_echo.py b/tests/slack_jabber/muc_echo.py deleted file mode 100644 index 80516d25e..000000000 --- a/tests/slack_jabber/muc_echo.py +++ /dev/null @@ -1,53 +0,0 @@ -import optparse -import sys -import time -import subprocess -import os - -import sleekxmpp - - -class Responder(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, room_password, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.room_password = room_password - self.finished = False - self.add_event_handler("session_start", self.start) - self.add_event_handler("groupchat_message", self.muc_message) - - self.tests = {} - - def muc_message(self, msg): - if msg['mucnick'] != self.nick: - self.send_message(mto=msg['from'].bare, - mbody="echo %s" % msg['body'], - mtype='groupchat') - - def start(self, event): - self.plugin['xep_0045'].joinMUC(self.room, self.nick, password=self.room_password, wait=True) - -class Client(sleekxmpp.ClientXMPP): - def __init__(self, jid, password, room, nick): - sleekxmpp.ClientXMPP.__init__(self, jid, password) - self.room = room - self.nick = nick - self.add_event_handler("session_start", self.start) - self.add_event_handler("groupchat_message", self.muc_message) - self.finished = False - - self.tests = {} - self.tests["echo_received"] = ["libcommuni: Send and receive messages", False] - - def muc_message(self, msg): - if msg['mucnick'] != self.nick: - if msg['body'] == "echo abc" or msg['body'] == " echo abc": - self.tests["echo_received"][1] = True - self.finished = True - - def start(self, event): - self.getRoster() - self.sendPresence() - self.plugin['xep_0045'].joinMUC(self.room, self.nick, wait=True) - self.send_message(mto=self.room, mbody="abc", mtype='groupchat') diff --git a/tests/start.py b/tests/start.py deleted file mode 100644 index 561ca0e65..000000000 --- a/tests/start.py +++ /dev/null @@ -1,315 +0,0 @@ -import optparse -import sys -import time -import subprocess -import os -import traceback - -import sleekxmpp -from importlib.machinery import SourceFileLoader -import logging - -#logging.basicConfig(level=logging.DEBUG, - #format='%(levelname)-8s %(message)s') - -def registerXMPPAccount(user, password): - responder = sleekxmpp.ClientXMPP(user, password) - responder.register_plugin('xep_0030') # Service Discovery - responder.register_plugin('xep_0077') - responder['feature_mechanisms'].unencrypted_plain = True - - if responder.connect(("127.0.0.1", 5222)): - responder.process(block=False) - else: - print("connect() failed") - sys.exit(1) - - -class BaseTestCase: - def __init__(self, env): - self.env = env - self.tests = [] - self.enabled = True - - def setup(self): - pass - - def teardown(self): - pass - - def finished(self): - return true - -# Default test case for one client and one responder -class ClientResponderTestCase(BaseTestCase): - def __init__(self, env, Client, Responder): - BaseTestCase.__init__(self, env) - self.env = env - self.Client = Client - self.Responder = Responder - self.enabled = True - - def setup(self): - #print str([self.env.responder_jid, self.env.responder_password, self.env.responder_room, self.env.responder_roompassword, self.env.responder_nick]) - self.responder = self.Responder(self.env.responder_jid, self.env.responder_password, self.env.responder_room, self.env.responder_roompassword, self.env.responder_nick) - self.responder.register_plugin('xep_0030') # Service Discovery - self.responder.register_plugin('xep_0045') # Multi-User Chat - self.responder.register_plugin('xep_0199') # XMPP Ping - self.responder['feature_mechanisms'].unencrypted_plain = True - - to = (self.env.spectrum.host, self.env.spectrum.port) - if self.env.responder_password != "password": - to = () # auto detect from jid - # used by some tests to connect responder to legacy network - if self.responder.connect(to): - self.responder.process(block=False) - else: - raise Exception("connect() failed") - - #print str([self.env.client_jid, self.env.client_password, self.env.client_room, self.env.client_nick]) - self.client = self.Client(self.env.client_jid, self.env.client_password, self.env.client_room, self.env.client_nick) - self.client.register_plugin('xep_0030') # Service Discovery - self.client.register_plugin('xep_0045') # Multi-User Chat - self.client.register_plugin('xep_0199') # XMPP Ping - self.client['feature_mechanisms'].unencrypted_plain = True - - time.sleep(1) - - to = (self.env.spectrum.host, self.env.spectrum.port) - if self.env.responder_password != "password": - to = ("127.0.0.1", 5222) - if self.client.connect(to): - self.client.process(block=False) - else: - raise Exception("connect() failed") - - def finished(self): - #print "client.finished="+str(self.client.finished) - #print "responder.finished="+str(self.responder.finished) - return self.client.finished and self.responder.finished - - def teardown(self): - self.tests = [] - if hasattr(self, 'client'): - self.tests += self.client.tests.values() - self.client.disconnect() - if hasattr(self, 'responder'): - self.tests += self.responder.tests.values() - self.responder.disconnect() - -class Params: - pass - -class BaseTest: - def __init__(self, config, server_mode, room): - self.config = config - self.server_mode = server_mode - - # Spectrum connection parameters - self.spectrum = Params() - self.spectrum.host = "127.0.0.1" - self.spectrum.port = 5223 - self.spectrum.hostname = "localhostxmpp" - - # Legacy network connection parameters (default: XMPP) - self.backend = Params() - self.backend.host = "127.0.0.1" - self.backend.port = 5222 - self.backend.hostname = "localhost" - - # User accounts - self.client_jid = "client@localhost" - self.client_password = "password" - self.client_nick = "client" - self.client_backend_jid = "" - self.client_backend_password = "" - self.responder_jid = "responder@localhost" - self.responder_password = "password" - self.responder_nick = "responder" - self.responder_backend_jid = "" - self.responder_backend_password = "" - self.room = room - self.client_room = room - self.responder_room = room - self.responder_roompassword = "" - - def skip_test(self, test): - return False - - def start(self, TestCaseFile): - # Use ClientResponder by default, but files may declare their own TestCase()s - try: - testCase = TestCaseFile.TestCase(self) - except (NameError, AttributeError): - testCase = ClientResponderTestCase(self, TestCaseFile.Client, TestCaseFile.Responder) - - # Let the test self-disqualify - if hasattr(testCase, 'enabled') and not testCase.enabled: - print("Skipped (self-disqualified).") - return True - - self.pre_test() - time.sleep(1) - try: - testCase.setup() - max_time = 60 - while not testCase.finished() and max_time > 0: - if callable(getattr(testCase, "step", None)): - testCase.step() - time.sleep(1) - max_time -= 1 - finally: - testCase.teardown() - self.post_test() - - ret = True - for v in testCase.tests: - if v[1]: - print(v[0] + ": PASSED") - else: - print(v[0] + ": FAILED") - ret = False - - if not ret: - os.system("cat spectrum2.log") - - return ret - - def pre_test(self): - os.system("../../spectrum/src/spectrum2 -n ./" + self.config + " > spectrum2.log &") - - def post_test(self): - os.system("killall -w spectrum2") - -class LibcommuniServerModeSingleServerConf(BaseTest): - def __init__(self): - BaseTest.__init__(self, "../libcommuni/irc_test.cfg", True, "#channel@localhost") - self.directory = "../libcommuni/" - - def skip_test(self, test): - if test in ["muc_join_nickname_used.py"]: - return True - return False - - def pre_test(self): - BaseTest.pre_test(self) - os.system("/usr/sbin/ngircd -f ../libcommuni/ngircd.conf &") - - def post_test(self): - os.system("killall -w ngircd 2>/dev/null") - os.system("killall -w spectrum2_libcommuni_backend 2>/dev/null") - BaseTest.post_test(self) - -class LibcommuniServerModeConf(BaseTest): - def __init__(self): - BaseTest.__init__(self, "../libcommuni/irc_test2.cfg", True, "#channel%localhost@localhost") - self.directory = "../libcommuni/" - - def pre_test(self): - BaseTest.pre_test(self) - os.system("/usr/sbin/ngircd -f ../libcommuni/ngircd.conf &") - - def post_test(self): - os.system("killall -w ngircd 2>/dev/null") - os.system("killall -w spectrum2_libcommuni_backend 2>/dev/null") - BaseTest.post_test(self) - -class JabberServerModeConf(BaseTest): - def __init__(self): - BaseTest.__init__(self, "../libpurple_jabber/jabber_test.cfg", True, "room%conference.localhost@localhostxmpp") - self.directory = "../libpurple_jabber/" - self.client_jid = "client%localhost@localhostxmpp" - self.client_backend_jid = "client@localhost" - self.client_backend_password = self.client_password - self.responder_jid = "responder%localhost@localhostxmpp" - self.responder_backend_jid = "responder@localhost" - self.responder_backend_password = self.responder_password - - def skip_test(self, test): - if test in ["muc_whois.py", "muc_change_topic.py"]: - return True - return False - - def pre_test(self): - os.system("cp ../libpurple_jabber/prefs.xml ./ -f >/dev/null") - BaseTest.pre_test(self) - os.system("prosody --config ../libpurple_jabber/prosody.cfg.lua >prosody.log &") - time.sleep(3) - os.system("../../spectrum_manager/src/spectrum2_manager -c ../libpurple_jabber/manager.conf localhostxmpp register client%localhost@localhostxmpp client@localhost password 2>/dev/null >/dev/null") - os.system("../../spectrum_manager/src/spectrum2_manager -c ../libpurple_jabber/manager.conf localhostxmpp register responder%localhost@localhostxmpp responder@localhost password 2>/dev/null >/dev/null") - - def post_test(self): - os.system("killall -w -r lua.* 2>/dev/null") - os.system("killall -w spectrum2_libpurple_backend 2>/dev/null") - BaseTest.post_test(self) - -class JabberSlackServerModeConf(BaseTest): - def __init__(self): - BaseTest.__init__(self, "../slack_jabber/jabber_slack_test.cfg", True, "room%conference.localhost@localhostxmpp") - self.directory = "../slack_jabber/" - self.client_jid = "client@localhost" - self.client_room = "room@conference.localhost" - # Implicitly forces responder to connect to slack.com instead of localhost - # by passing a nonstandard responder_roompassword - self.responder_jid = "owner@spectrum2tests.xmpp.slack.com" - self.responder_password = "spectrum2tests.e2zJwtKjLhLmt14VsMKq" - self.responder_room = "spectrum2_room@conference.spectrum2tests.xmpp.slack.com" - self.responder_nick = "owner" - self.responder_roompassword = "spectrum2tests.e2zJwtKjLhLmt14VsMKq" - - def skip_test(self, test): - os.system("cp ../slack_jabber/slack.sql .") - if test.find("bad_password") != -1: - print("Changing password to 'badpassword'") - os.system("sqlite3 slack.sql \"UPDATE users SET password='badpassword' WHERE id=1\"") - return False - - def pre_test(self): - BaseTest.pre_test(self) - os.system("prosody --config ../slack_jabber/prosody.cfg.lua > prosody.log &") - - def post_test(self): - os.system("killall -w -r lua.* 2>/dev/null") - os.system("killall -w spectrum2_libpurple_backend 2>/dev/null") - BaseTest.post_test(self) - -configurations = [] -configurations.append(LibcommuniServerModeSingleServerConf()) -configurations.append(LibcommuniServerModeConf()) -configurations.append(JabberServerModeConf()) -#configurations.append(JabberSlackServerModeConf()) -#configurations.append(TwitterServerModeConf()) - -exitcode = 0 - -# Pass file names to run only those tests -test_list = sys.argv[1:] - -for conf in configurations: - for f in os.listdir(conf.directory): - if (len(test_list) > 0) and not (f in test_list): - continue - if not f.endswith(".py") or f == "start.py": - continue - - if len(sys.argv) == 2 and sys.argv[1] != f: - continue - - print(conf.__class__.__name__ + ": Starting " + f + " test ...") - # Modules must have distinct module names or their clases will be merged by loader! - modulename = (conf.directory+f).replace("/","_").replace("\\","_").replace(".","_") - test = SourceFileLoader(modulename, conf.directory + f).load_module() - - if conf.skip_test(f): - print("Skipped.") - continue - try: - ret = conf.start(test) - except Exception as e: - print("Test ended in exception.") - traceback.print_exc() - sys.exit(-1) - if not ret: - exitcode = -1 - -sys.exit(exitcode)