aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--SConstruct6
-rw-r--r--tests/net/smtp/SMTPResponseTest.cpp189
-rw-r--r--tests/net/smtp/SMTPTransportTest.cpp256
-rw-r--r--tests/testRunner.cpp4
-rw-r--r--tests/testUtils.cpp183
-rw-r--r--tests/testUtils.hpp110
6 files changed, 745 insertions, 3 deletions
diff --git a/SConstruct b/SConstruct
index 07fc4cc1..d85d36ab 100644
--- a/SConstruct
+++ b/SConstruct
@@ -333,6 +333,7 @@ libvmimetest_common = [
libvmimetest_sources = [
'tests/testRunner.cpp',
+ 'tests/testUtils.cpp',
# ============================== Parser ==============================
'tests/parser/attachmentHelperTest.cpp',
'tests/parser/bodyPartTest.cpp',
@@ -360,7 +361,10 @@ libvmimetest_sources = [
'tests/misc/importanceHelperTest.cpp',
# ============================= Security =============================
'tests/security/digest/md5Test.cpp',
- 'tests/security/digest/sha1Test.cpp'
+ 'tests/security/digest/sha1Test.cpp',
+ # =============================== Net ================================
+ 'tests/net/smtp/SMTPTransportTest.cpp',
+ 'tests/net/smtp/SMTPResponseTest.cpp'
]
libvmime_autotools = [
diff --git a/tests/net/smtp/SMTPResponseTest.cpp b/tests/net/smtp/SMTPResponseTest.cpp
new file mode 100644
index 00000000..495dd437
--- /dev/null
+++ b/tests/net/smtp/SMTPResponseTest.cpp
@@ -0,0 +1,189 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2006 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#include "tests/testUtils.hpp"
+
+#include "vmime/net/smtp/SMTPResponse.hpp"
+
+
+#define VMIME_TEST_SUITE SMTPResponseTest
+#define VMIME_TEST_SUITE_MODULE "Net/SMTP"
+
+
+VMIME_TEST_SUITE_BEGIN
+
+ VMIME_TEST_LIST_BEGIN
+ VMIME_TEST(testSingleLineResponse)
+ VMIME_TEST(testSingleLineResponseLF)
+ VMIME_TEST(testMultiLineResponse)
+ VMIME_TEST(testMultiLineResponseDifferentCode)
+ VMIME_TEST(testIncompleteMultiLineResponse)
+ VMIME_TEST(testIntermediateResponse)
+ VMIME_TEST(testNoResponseText)
+ VMIME_TEST_LIST_END
+
+
+ void testSingleLineResponse()
+ {
+ vmime::ref <testSocket> socket = vmime::create <testSocket>();
+ vmime::ref <vmime::net::timeoutHandler> toh =
+ vmime::create <testTimeoutHandler>();
+
+ socket->localSend("123 Response Text\r\n");
+
+ vmime::ref <vmime::net::smtp::SMTPResponse> resp =
+ vmime::net::smtp::SMTPResponse::readResponse(socket, toh);
+
+ VASSERT_EQ("Code", 123, resp->getCode());
+ VASSERT_EQ("Lines", 1, resp->getLineCount());
+ VASSERT_EQ("Text", "Response Text", resp->getText());
+ }
+
+ void testSingleLineResponseLF()
+ {
+ vmime::ref <testSocket> socket = vmime::create <testSocket>();
+ vmime::ref <vmime::net::timeoutHandler> toh =
+ vmime::create <testTimeoutHandler>();
+
+ socket->localSend("123 Response Text\n");
+
+ vmime::ref <vmime::net::smtp::SMTPResponse> resp =
+ vmime::net::smtp::SMTPResponse::readResponse(socket, toh);
+
+ VASSERT_EQ("Code", 123, resp->getCode());
+ VASSERT_EQ("Lines", 1, resp->getLineCount());
+ VASSERT_EQ("Text", "Response Text", resp->getText());
+ }
+
+ void testMultiLineResponse()
+ {
+ vmime::ref <testSocket> socket = vmime::create <testSocket>();
+ vmime::ref <vmime::net::timeoutHandler> toh =
+ vmime::create <testTimeoutHandler>();
+
+ socket->localSend
+ (
+ "123-Response\r\n"
+ "123 Text\r\n"
+ );
+
+ vmime::ref <vmime::net::smtp::SMTPResponse> resp =
+ vmime::net::smtp::SMTPResponse::readResponse(socket, toh);
+
+ VASSERT_EQ("Code", 123, resp->getCode());
+ VASSERT_EQ("Lines", 2, resp->getLineCount());
+ VASSERT_EQ("Text", "Response\nText", resp->getText());
+
+ VASSERT_EQ("Code", 123, resp->getLineAt(0).getCode());
+ VASSERT_EQ("Text", "Response", resp->getLineAt(0).getText());
+
+ VASSERT_EQ("Code", 123, resp->getLineAt(1).getCode());
+ VASSERT_EQ("Text", "Text", resp->getLineAt(1).getText());
+ }
+
+ void testMultiLineResponseDifferentCode()
+ {
+ vmime::ref <testSocket> socket = vmime::create <testSocket>();
+ vmime::ref <vmime::net::timeoutHandler> toh =
+ vmime::create <testTimeoutHandler>();
+
+ socket->localSend
+ (
+ "123-Response\r\n"
+ "456 Text\r\n"
+ );
+
+ vmime::ref <vmime::net::smtp::SMTPResponse> resp =
+ vmime::net::smtp::SMTPResponse::readResponse(socket, toh);
+
+ VASSERT_EQ("Code", 0, resp->getCode());
+ VASSERT_EQ("Lines", 2, resp->getLineCount());
+ VASSERT_EQ("Text", "Response\nText", resp->getText());
+
+ VASSERT_EQ("Code", 123, resp->getLineAt(0).getCode());
+ VASSERT_EQ("Text", "Response", resp->getLineAt(0).getText());
+
+ VASSERT_EQ("Code", 456, resp->getLineAt(1).getCode());
+ VASSERT_EQ("Text", "Text", resp->getLineAt(1).getText());
+ }
+
+ void testIncompleteMultiLineResponse()
+ {
+ vmime::ref <testSocket> socket = vmime::create <testSocket>();
+ vmime::ref <vmime::net::timeoutHandler> toh =
+ vmime::create <testTimeoutHandler>(1);
+
+ socket->localSend
+ (
+ "123-Response\r\n"
+ "123-Text\r\n"
+ // Missing data
+ );
+
+ VASSERT_THROW("Incomplete response",
+ vmime::net::smtp::SMTPResponse::readResponse(socket, toh),
+ vmime::exceptions::operation_timed_out);
+ }
+
+ void testIntermediateResponse()
+ {
+ vmime::ref <testSocket> socket = vmime::create <testSocket>();
+ vmime::ref <vmime::net::timeoutHandler> toh =
+ vmime::create <testTimeoutHandler>(1);
+
+ socket->localSend
+ (
+ "334\r\n"
+ "More information\r\n"
+ );
+
+ vmime::ref <vmime::net::smtp::SMTPResponse> resp =
+ vmime::net::smtp::SMTPResponse::readResponse(socket, toh);
+
+ VASSERT_EQ("Code", 334, resp->getCode());
+ VASSERT_EQ("Lines", 1, resp->getLineCount());
+ VASSERT_EQ("Text", "More information", resp->getText());
+ }
+
+ void testNoResponseText()
+ {
+ vmime::ref <testSocket> socket = vmime::create <testSocket>();
+ vmime::ref <vmime::net::timeoutHandler> toh =
+ vmime::create <testTimeoutHandler>(1);
+
+ socket->localSend
+ (
+ "250\r\n"
+ );
+
+ vmime::ref <vmime::net::smtp::SMTPResponse> resp =
+ vmime::net::smtp::SMTPResponse::readResponse(socket, toh);
+
+ VASSERT_EQ("Code", 250, resp->getCode());
+ VASSERT_EQ("Lines", 1, resp->getLineCount());
+ VASSERT_EQ("Text", "", resp->getText());
+ }
+
+
+VMIME_TEST_SUITE_END
+
diff --git a/tests/net/smtp/SMTPTransportTest.cpp b/tests/net/smtp/SMTPTransportTest.cpp
new file mode 100644
index 00000000..a69c2144
--- /dev/null
+++ b/tests/net/smtp/SMTPTransportTest.cpp
@@ -0,0 +1,256 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2006 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#include "tests/testUtils.hpp"
+
+
+#define VMIME_TEST_SUITE SMTPTransportTest
+#define VMIME_TEST_SUITE_MODULE "Net/SMTP"
+
+
+class greetingErrorSMTPTestSocket;
+class MAILandRCPTSMTPTestSocket;
+
+
+VMIME_TEST_SUITE_BEGIN
+
+ VMIME_TEST_LIST_BEGIN
+ VMIME_TEST(testGreetingError)
+ VMIME_TEST(testMAILandRCPT)
+ VMIME_TEST_LIST_END
+
+
+ void testGreetingError()
+ {
+ vmime::ref <vmime::net::session> session =
+ vmime::create <vmime::net::session>();
+
+ vmime::ref <vmime::net::transport> tr = session->getTransport
+ (vmime::utility::url("smtp://localhost"));
+
+ tr->setSocketFactory(vmime::create <testSocketFactory <greetingErrorSMTPTestSocket> >());
+ tr->setTimeoutHandlerFactory(vmime::create <testTimeoutHandlerFactory>());
+
+ VASSERT_THROW("Connection", tr->connect(),
+ vmime::exceptions::connection_greeting_error);
+ }
+
+ void testMAILandRCPT()
+ {
+ vmime::ref <vmime::net::session> session =
+ vmime::create <vmime::net::session>();
+
+ vmime::ref <vmime::net::transport> tr = session->getTransport
+ (vmime::utility::url("smtp://localhost"));
+
+ tr->setSocketFactory(vmime::create <testSocketFactory <MAILandRCPTSMTPTestSocket> >());
+ tr->setTimeoutHandlerFactory(vmime::create <testTimeoutHandlerFactory>());
+
+ VASSERT_NO_THROW("Connection", tr->connect());
+
+ vmime::mailbox exp("[email protected]");
+
+ vmime::mailboxList recips;
+ recips.appendMailbox(vmime::create <vmime::mailbox>("[email protected]"));
+ recips.appendMailbox(vmime::create <vmime::mailbox>("[email protected]"));
+ recips.appendMailbox(vmime::create <vmime::mailbox>("[email protected]"));
+
+ vmime::string data("Message data");
+ vmime::utility::inputStreamStringAdapter is(data);
+
+ tr->send(exp, recips, is, 0);
+ }
+
+VMIME_TEST_SUITE_END
+
+
+/** Accepts connection and fails on greeting.
+ */
+class greetingErrorSMTPTestSocket : public lineBasedTestSocket
+{
+public:
+
+ void onConnected()
+ {
+ localSend("421 test.vmime.org Service not available, closing transmission channel\r\n");
+ disconnect();
+ }
+
+ void processCommand()
+ {
+ if (!haveMoreLines())
+ return;
+
+ getNextLine();
+
+ localSend("502 Command not implemented\r\n");
+ processCommand();
+ }
+};
+
+
+/** SMTP test server 1.
+ *
+ * Test send().
+ * Ensure MAIL and RCPT commands are sent correctly.
+ */
+class MAILandRCPTSMTPTestSocket : public lineBasedTestSocket
+{
+public:
+
+ MAILandRCPTSMTPTestSocket()
+ {
+ m_recipients.insert("[email protected]");
+ m_recipients.insert("[email protected]");
+ m_recipients.insert("[email protected]");
+
+ m_state = STATE_NOT_CONNECTED;
+ }
+
+ void onConnected()
+ {
+ localSend("220 test.vmime.org Service ready\r\n");
+ processCommand();
+
+ m_state = STATE_COMMAND;
+ }
+
+ void processCommand()
+ {
+ if (!haveMoreLines())
+ return;
+
+ vmime::string line = getNextLine();
+ std::istringstream iss(line);
+
+ switch (m_state)
+ {
+ case STATE_NOT_CONNECTED:
+
+ localSend("451 Requested action aborted: invalid state\r\n");
+ break;
+
+ case STATE_COMMAND:
+ {
+ std::string cmd;
+ iss >> cmd;
+
+ if (cmd.empty())
+ {
+ localSend("500 Syntax error, command unrecognized\r\n");
+ }
+ else if (cmd == "HELO")
+ {
+ localSend("250 OK\r\n");
+ }
+ else if (cmd == "MAIL")
+ {
+ VASSERT_EQ("MAIL", std::string("MAIL FROM: <[email protected]>"), line);
+
+ localSend("250 OK\r\n");
+ }
+ else if (cmd == "RCPT")
+ {
+ const vmime::string::size_type lt = line.find('<');
+ const vmime::string::size_type gt = line.find('>');
+
+ VASSERT("RCPT <", lt != vmime::string::npos);
+ VASSERT("RCPT >", gt != vmime::string::npos);
+ VASSERT("RCPT ><", gt >= lt);
+
+ const vmime::string recip = vmime::string
+ (line.begin() + lt + 1, line.begin() + gt);
+
+ std::set <vmime::string>::iterator it =
+ m_recipients.find(recip);
+
+ VASSERT(std::string("Recipient not found: '") + recip + "'",
+ it != m_recipients.end());
+
+ m_recipients.erase(it);
+
+ localSend("250 OK, recipient accepted\r\n");
+ }
+ else if (cmd == "DATA")
+ {
+ VASSERT("All recipients", m_recipients.empty());
+
+ localSend("354 Ready to accept data; end with <CRLF>.<CRLF>\r\n");
+
+ m_state = STATE_DATA;
+ m_msgData.clear();
+ }
+ else if (cmd == "NOOP")
+ {
+ localSend("250 Completed\r\n");
+ }
+ else if (cmd == "QUIT")
+ {
+ localSend("221 test.vmime.org Service closing transmission channel\r\n");
+ }
+ else
+ {
+ localSend("502 Command not implemented\r\n");
+ }
+
+ break;
+ }
+ case STATE_DATA:
+ {
+ if (line == ".")
+ {
+ VASSERT_EQ("Data", "Message data\r\n", m_msgData);
+
+ localSend("250 Message accepted for delivery\r\n");
+ m_state = STATE_COMMAND;
+ }
+ else
+ {
+ m_msgData += line + "\r\n";
+ }
+
+ break;
+ }
+
+ }
+
+ processCommand();
+ }
+
+private:
+
+ enum State
+ {
+ STATE_NOT_CONNECTED,
+ STATE_COMMAND,
+ STATE_DATA
+ };
+
+ int m_state;
+
+ std::set <vmime::string> m_recipients;
+
+ std::string m_msgData;
+};
+
+
diff --git a/tests/testRunner.cpp b/tests/testRunner.cpp
index c49090f0..8700ea16 100644
--- a/tests/testRunner.cpp
+++ b/tests/testRunner.cpp
@@ -210,7 +210,7 @@ int main(int argc, char* argv[])
// Run the tests
if (xmlOutput)
{
- // Get the test suites from the registry and add them to the list of test to run
+ // Get the test suites from the registry and add them to the list of tests to run
CppUnit::TestRunner runner;
for (unsigned int i = 0 ; i < getTestModules().size() ; ++i)
@@ -231,7 +231,7 @@ int main(int argc, char* argv[])
xmlListener->output(std::cout);
- // Return error code 1 if the one of test failed
+ // Return error code 1 if a test failed
return result.wasSuccessful() ? 0 : 1;
}
else
diff --git a/tests/testUtils.cpp b/tests/testUtils.cpp
new file mode 100644
index 00000000..34d945c9
--- /dev/null
+++ b/tests/testUtils.cpp
@@ -0,0 +1,183 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002-2005 Vincent Richard <[email protected]>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of
+// the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// Linking this library statically or dynamically with other modules is making
+// a combined work based on this library. Thus, the terms and conditions of
+// the GNU General Public License cover the whole combination.
+//
+
+#include "testUtils.hpp"
+
+
+
+// testSocket
+
+void testSocket::connect(const vmime::string& address, const vmime::port_t port)
+{
+ m_address = address;
+ m_port = port;
+ m_connected = true;
+
+ onConnected();
+}
+
+
+void testSocket::disconnect()
+{
+ m_address.clear();
+ m_port = 0;
+ m_connected = false;
+}
+
+
+const bool testSocket::isConnected() const
+{
+ return m_connected;
+}
+
+
+void testSocket::receive(vmime::string& buffer)
+{
+ buffer = m_inBuffer;
+ m_inBuffer.clear();
+}
+
+
+void testSocket::send(const vmime::string& buffer)
+{
+ m_outBuffer += buffer;
+
+ onDataReceived();
+}
+
+
+const int testSocket::receiveRaw(char* buffer, const int count)
+{
+ const int n = std::min(count, static_cast <int>(m_inBuffer.size()));
+
+ std::copy(m_inBuffer.begin(), m_inBuffer.begin() + n, buffer);
+ m_inBuffer.erase(m_inBuffer.begin(), m_inBuffer.begin() + n);
+
+ return n;
+}
+
+
+void testSocket::sendRaw(const char* buffer, const int count)
+{
+ send(vmime::string(buffer, count));
+}
+
+
+void testSocket::localSend(const vmime::string& buffer)
+{
+ m_inBuffer += buffer;
+}
+
+
+void testSocket::localReceive(vmime::string& buffer)
+{
+ buffer = m_outBuffer;
+ m_outBuffer.clear();
+}
+
+
+void testSocket::onDataReceived()
+{
+ // Override
+}
+
+
+void testSocket::onConnected()
+{
+ // Override
+}
+
+
+// lineBasedTestSocket
+
+void lineBasedTestSocket::onDataReceived()
+{
+ vmime::string chunk;
+ localReceive(chunk);
+
+ m_buffer += chunk;
+
+ vmime::string::size_type eol;
+
+ while ((eol = m_buffer.find('\n')) != vmime::string::npos)
+ {
+ vmime::string line(std::string(m_buffer.begin(), m_buffer.begin() + eol));
+
+ if (!line.empty() && line[line.length() - 1] == '\r')
+ line.erase(line.end() - 1, line.end());
+
+ m_lines.push_back(line);
+ m_buffer.erase(m_buffer.begin(), m_buffer.begin() + eol + 1);
+ }
+
+ while (!m_lines.empty())
+ processCommand();
+}
+
+
+const vmime::string lineBasedTestSocket::getNextLine()
+{
+ const vmime::string line = m_lines.front();
+ m_lines.erase(m_lines.begin(), m_lines.begin() + 1);
+ return line;
+}
+
+const bool lineBasedTestSocket::haveMoreLines() const
+{
+ return !m_lines.empty();
+}
+
+
+// testTimeoutHandler
+
+testTimeoutHandler::testTimeoutHandler(const unsigned int delay)
+ : m_delay(delay), m_start(0)
+{
+}
+
+
+const bool testTimeoutHandler::isTimeOut()
+{
+ return (vmime::platformDependant::getHandler()->getUnixTime() - m_start) >= m_delay;
+}
+
+
+void testTimeoutHandler::resetTimeOut()
+{
+ m_start = vmime::platformDependant::getHandler()->getUnixTime();
+}
+
+
+const bool testTimeoutHandler::handleTimeOut()
+{
+ return false;
+}
+
+
+// testTimeoutHandlerFactory : public vmime::net::timeoutHandlerFactory
+
+vmime::ref <vmime::net::timeoutHandler> testTimeoutHandlerFactory::create()
+{
+ return vmime::create <testTimeoutHandler>();
+}
+
diff --git a/tests/testUtils.hpp b/tests/testUtils.hpp
index 9b8a5192..c2185a08 100644
--- a/tests/testUtils.hpp
+++ b/tests/testUtils.hpp
@@ -193,3 +193,113 @@ inline std::ostream& operator<<(std::ostream& os, const vmime::datetime& d)
}
+
+
+// Used to test network features.
+//
+// This works like a local pipe: client reads and writes data using receive()
+// and send(). Server reads incoming data with localReceive() and sends data
+// to client with localSend().
+
+class testSocket : public vmime::net::socket
+{
+public:
+
+ void connect(const vmime::string& address, const vmime::port_t port);
+ void disconnect();
+
+ const bool isConnected() const;
+
+ void receive(vmime::string& buffer);
+ void send(const vmime::string& buffer);
+
+ const int receiveRaw(char* buffer, const int count);
+ void sendRaw(const char* buffer, const int count);
+
+ /** Send data to client.
+ *
+ * @buffer data to send
+ */
+ void localSend(const vmime::string& buffer);
+
+ /** Receive data from client.
+ *
+ * @buffer buffer in which to store received data
+ */
+ void localReceive(vmime::string& buffer);
+
+protected:
+
+ /** Called when the client has sent some data.
+ */
+ virtual void onDataReceived();
+
+ /** Called when the client has connected.
+ */
+ virtual void onConnected();
+
+private:
+
+ vmime::string m_address;
+ vmime::port_t m_port;
+ bool m_connected;
+
+ vmime::string m_inBuffer;
+ vmime::string m_outBuffer;
+};
+
+
+template <typename T>
+class testSocketFactory : public vmime::net::socketFactory
+{
+public:
+
+ vmime::ref <vmime::net::socket> create()
+ {
+ return vmime::create <T>();
+ }
+};
+
+
+class lineBasedTestSocket : public testSocket
+{
+public:
+
+ void onDataReceived();
+
+ const vmime::string getNextLine();
+ const bool haveMoreLines() const;
+
+ virtual void processCommand() = 0;
+
+private:
+
+ std::vector <vmime::string> m_lines;
+ std::string m_buffer;
+};
+
+
+class testTimeoutHandler : public vmime::net::timeoutHandler
+{
+public:
+
+ testTimeoutHandler(const unsigned int delay = 3);
+
+ const bool isTimeOut();
+ void resetTimeOut();
+ const bool handleTimeOut();
+
+private:
+
+ unsigned int m_delay;
+ unsigned int m_start;
+};
+
+
+class testTimeoutHandlerFactory : public vmime::net::timeoutHandlerFactory
+{
+public:
+
+ vmime::ref <vmime::net::timeoutHandler> create();
+};
+