File indexing completed on 2024-05-12 17:15:22
0001 /* 0002 Copyright (C) 2013 Andreas Hartmetz <ahartmetz@gmail.com> 0003 0004 This library is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LGPL. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 Boston, MA 02110-1301, USA. 0018 0019 Alternatively, this file is available under the Mozilla Public License 0020 Version 1.1. You may obtain a copy of the License at 0021 http://www.mozilla.org/MPL/ 0022 */ 0023 0024 #include "arguments.h" 0025 #include "connectaddress.h" 0026 #include "error.h" 0027 #include "eventdispatcher.h" 0028 #include "imessagereceiver.h" 0029 #include "message.h" 0030 #include "pendingreply.h" 0031 #include "testutil.h" 0032 #include "connection.h" 0033 0034 #include <fcntl.h> 0035 #include <unistd.h> 0036 0037 #include <cstring> 0038 #include <iostream> 0039 0040 static void test_signatureHeader() 0041 { 0042 Message msg; 0043 Arguments::Writer writer; 0044 writer.writeByte(123); 0045 writer.writeUint64(1); 0046 msg.setArguments(writer.finish()); 0047 TEST(msg.signature() == "yt"); 0048 } 0049 0050 class PrintAndTerminateClient : public IMessageReceiver 0051 { 0052 public: 0053 void handleSpontaneousMessageReceived(Message msg, Connection *connection) override 0054 { 0055 std::cout << msg.prettyPrint(); 0056 connection->eventDispatcher()->interrupt(); 0057 } 0058 }; 0059 0060 class PrintAndReplyClient : public IMessageReceiver 0061 { 0062 public: 0063 void handleSpontaneousMessageReceived(Message msg, Connection *connection) override 0064 { 0065 std::cout << msg.prettyPrint(); 0066 connection->sendNoReply(Message::createErrorReplyTo(msg, "Unable to get out of hammock!")); 0067 //connection->eventDispatcher()->interrupt(); 0068 } 0069 }; 0070 0071 // used during implementation, is supposed to not crash and be valgrind-clean afterwards 0072 void testBasic(const ConnectAddress &clientAddress) 0073 { 0074 EventDispatcher dispatcher; 0075 0076 ConnectAddress serverAddress = clientAddress; 0077 serverAddress.setRole(ConnectAddress::Role::PeerServer); 0078 0079 Connection serverConnection(&dispatcher, serverAddress); 0080 std::cout << "Created server connection. " << &serverConnection << std::endl; 0081 Connection clientConnection(&dispatcher, clientAddress); 0082 std::cout << "Created client connection. " << &clientConnection << std::endl; 0083 0084 PrintAndReplyClient printAndReplyClient; 0085 serverConnection.setSpontaneousMessageReceiver(&printAndReplyClient); 0086 0087 PrintAndTerminateClient printAndTerminateClient; 0088 clientConnection.setSpontaneousMessageReceiver(&printAndTerminateClient); 0089 0090 Message msg = Message::createCall("/foo", "org.foo.interface", "laze"); 0091 Arguments::Writer writer; 0092 writer.writeString("couch"); 0093 msg.setArguments(writer.finish()); 0094 0095 clientConnection.sendNoReply(std::move(msg)); 0096 0097 while (dispatcher.poll()) { 0098 } 0099 } 0100 0101 void testMessageLength() 0102 { 0103 static const uint32 bufferSize = Arguments::MaxArrayLength + 1024; 0104 byte *buffer = static_cast<byte *>(malloc(bufferSize)); 0105 memset(buffer, 0, bufferSize); 0106 for (int i = 0; i < 2; i++) { 0107 const bool makeTooLong = i == 1; 0108 0109 Arguments::Writer writer; 0110 writer.writePrimitiveArray(Arguments::Byte, chunk(buffer, Arguments::MaxArrayLength)); 0111 0112 // Our minimal Message is going to have the following variable headers (in that order): 0113 // Array: 4 byte length prefix 0114 // PathHeader: 4 byte length prefix 0115 // MethodHeader: 4 byte length prefix 0116 // SignatureHeader: 1 byte length prefix 0117 0118 // This is VERY tedious to calculate, so let's just take it as an experimentally determined value 0119 uint32 left = Arguments::MaxMessageLength - Arguments::MaxArrayLength - 72; 0120 if (makeTooLong) { 0121 left += 1; 0122 } 0123 writer.writePrimitiveArray(Arguments::Byte, chunk(buffer, left)); 0124 0125 Message msg = Message::createCall("/a", "x"); 0126 msg.setSerial(1); 0127 msg.setArguments(writer.finish()); 0128 std::vector<byte> saved = msg.save(); 0129 TEST(msg.error().isError() == makeTooLong); 0130 } 0131 } 0132 0133 enum { 0134 // a small integer could be confused with an index into the fd array (in the implementation), 0135 // so make it large 0136 DummyFdOffset = 1000000 0137 }; 0138 0139 #ifdef __unix__ 0140 static Arguments createArgumentsWithDummyFileDescriptors(uint fdCount) 0141 { 0142 Arguments::Writer writer; 0143 for (uint i = 0; i < fdCount; i++) { 0144 writer.writeUnixFd(DummyFdOffset - i); 0145 } 0146 return writer.finish(); 0147 } 0148 0149 void testFileDescriptorsInArguments() 0150 { 0151 // Note: This replaces round-trip tests with file descriptors in tst_arguments. 0152 // A full roundtrip test must go through Message due to the out-of-band way that file 0153 // descriptors are stored (which is so because they are also transmitted out-of-band). 0154 Message msg = Message::createCall("/foo", "org.foo.interface", "doNothing"); 0155 for (uint i = 0; i < 4; i++) { 0156 msg.setArguments(createArgumentsWithDummyFileDescriptors(i)); 0157 { 0158 // const ref to arguments 0159 const Arguments &args = msg.arguments(); 0160 Arguments::Reader reader(args); 0161 for (uint j = 0; j < i; j++) { 0162 TEST(reader.readUnixFd() == int(DummyFdOffset - j)); 0163 TEST(reader.isValid()); 0164 } 0165 TEST(reader.isFinished()); 0166 } 0167 { 0168 // copy of arguments 0169 Arguments args = msg.arguments(); 0170 Arguments::Reader reader(args); 0171 for (uint j = 0; j < i; j++) { 0172 TEST(reader.readUnixFd() == int(DummyFdOffset - j)); 0173 TEST(reader.isValid()); 0174 } 0175 TEST(reader.isFinished()); 0176 } 0177 } 0178 } 0179 0180 void testTooManyFileDescriptors() 0181 { 0182 // TODO re-think what is the best place to catch too many file descriptors... 0183 Arguments::Writer writer; 0184 } 0185 0186 void testFileDescriptorsHeader() 0187 { 0188 Message msg = Message::createCall("/foo", "org.foo.interface", "doNothing"); 0189 for (uint i = 0; i < 4; i++) { 0190 msg.setArguments(createArgumentsWithDummyFileDescriptors(i)); 0191 TEST(msg.unixFdCount() == i); 0192 } 0193 } 0194 0195 enum { 0196 // for pipe2() file descriptor array 0197 ReadSide = 0, 0198 WriteSide = 1, 0199 // how many file descriptors to send in test 0200 FdCountToSend = 10 0201 }; 0202 0203 class FileDescriptorTestReceiver : public IMessageReceiver 0204 { 0205 public: 0206 void handleSpontaneousMessageReceived(Message msg, Connection *connection) override 0207 { 0208 // we're on the session bus, so we'll receive all kinds of notifications we don't care about here 0209 if (msg.type() != Message::MethodCallMessage 0210 || msg.method() != "testFileDescriptorsForDataTransfer") { 0211 return; 0212 } 0213 0214 Arguments::Reader reader(msg.arguments()); 0215 for (uint i = 0; i < FdCountToSend; i++) { 0216 int fd = reader.readUnixFd(); 0217 uint readBuf = 12345; 0218 ::read(fd, &readBuf, sizeof(uint)); 0219 ::close(fd); 0220 TEST(readBuf == i); 0221 } 0222 Message reply = Message::createReplyTo(msg); 0223 connection->sendNoReply(std::move(reply)); 0224 } 0225 }; 0226 0227 void testFileDescriptorsForDataTransfer() 0228 { 0229 EventDispatcher eventDispatcher; 0230 Connection conn(&eventDispatcher, ConnectAddress::StandardBus::Session); 0231 conn.waitForConnectionEstablished(); 0232 TEST(conn.isConnected()); 0233 0234 int pipeFds[2 * FdCountToSend]; 0235 0236 Message msg = Message::createCall("/foo", "org.foo.interface", "testFileDescriptorsForDataTransfer"); 0237 msg.setDestination(conn.uniqueName()); 0238 0239 Arguments::Writer writer; 0240 for (uint i = 0; i < FdCountToSend; i++) { 0241 TEST(pipe2(pipeFds + 2 * i, O_NONBLOCK) == 0); 0242 // write into write side of the pipe... will be read when the message is received back from bus 0243 ::write(pipeFds[2 * i + WriteSide], &i, sizeof(uint)); 0244 0245 writer.writeUnixFd(pipeFds[2 * i + ReadSide]); 0246 } 0247 0248 msg.setArguments(writer.finish()); 0249 0250 PendingReply reply = conn.send(std::move(msg), 500 /* fail quickly */); 0251 FileDescriptorTestReceiver fdTestReceiver; 0252 conn.setSpontaneousMessageReceiver(&fdTestReceiver); 0253 0254 while (!reply.isFinished()) { 0255 eventDispatcher.poll(); 0256 } 0257 0258 if (conn.supportedFileDescriptorsPerMessage() >= FdCountToSend) { 0259 TEST(reply.hasNonErrorReply()); // otherwise timeout, the message exchange failed somehow 0260 } else { 0261 TEST(!reply.hasNonErrorReply()); 0262 TEST(reply.error().code() == Error::SendingTooManyUnixFds); 0263 for (uint i = 0; i < FdCountToSend; i++) { 0264 ::close(pipeFds[2 * i + ReadSide]); 0265 } 0266 } 0267 0268 for (uint i = 0; i < FdCountToSend; i++) { 0269 ::close(pipeFds[2 * i + WriteSide]); 0270 } 0271 } 0272 #endif 0273 0274 void testAssignment() 0275 { 0276 Message msg1 = Message::createCall("/foo", "org.foo.bar", "someMethod"); 0277 msg1.setSender("sender1"); 0278 Message msg2 = Message::createSignal("/bar", "org.xyz.abc", "thingHappened"); 0279 msg2.setReplySerial(1234); 0280 0281 msg2 = msg1; 0282 TEST(msg2.type() == Message::MethodCallMessage); 0283 TEST(msg2.path() == "/foo"); 0284 TEST(msg2.interface() == "org.foo.bar"); 0285 TEST(msg2.method() == "someMethod"); 0286 TEST(msg2.sender() == "sender1"); 0287 TEST(msg2.replySerial() == 0); 0288 } 0289 0290 int main(int, char *[]) 0291 { 0292 test_signatureHeader(); 0293 #ifdef __linux__ 0294 { 0295 ConnectAddress clientAddress; 0296 clientAddress.setType(ConnectAddress::Type::AbstractUnixPath); 0297 clientAddress.setRole(ConnectAddress::Role::PeerClient); 0298 clientAddress.setPath("dferry.Test.Message"); 0299 testBasic(clientAddress); 0300 } 0301 #endif 0302 // TODO: SocketType::Unix works on any Unix-compatible OS, but we'll need to construct a path 0303 { 0304 ConnectAddress clientAddress; 0305 clientAddress.setType(ConnectAddress::Type::Tcp); 0306 clientAddress.setPort(6800); 0307 clientAddress.setRole(ConnectAddress::Role::PeerClient); 0308 testBasic(clientAddress); 0309 } 0310 0311 testMessageLength(); 0312 0313 #ifdef __unix__ 0314 testFileDescriptorsInArguments(); 0315 testTooManyFileDescriptors(); 0316 testFileDescriptorsHeader(); 0317 testFileDescriptorsForDataTransfer(); 0318 #endif 0319 testAssignment(); 0320 0321 // TODO testSaveLoad(); 0322 // TODO testDeepCopy(); 0323 std::cout << "\nNote that the hammock error is part of the test.\nPassed!\n"; 0324 }