File indexing completed on 2024-05-12 17:15:20

0001 /*
0002    Copyright (C) 2018 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 <iostream>
0035 
0036 static const char *s_testMethod = "dferryTestingMethod";
0037 
0038 class ReplierReceiver : public IMessageReceiver
0039 {
0040 public:
0041     void handleSpontaneousMessageReceived(Message msg, Connection *connection) override
0042     {
0043         std::cerr << "   Replier here. Yo, got it!" << std::endl;
0044         // we're on the session bus, so we'll receive all kinds of notifications we don't care about here
0045         if (msg.type() != Message::MethodCallMessage || msg.method() != s_testMethod) {
0046             return;
0047         }
0048         // TODO also generate a malformed reply and see what happens
0049         Message reply = Message::createReplyTo(msg);
0050         connection->sendNoReply(std::move(reply));
0051     }
0052 };
0053 
0054 enum { StepsCount = 10 };
0055 
0056 void test_errorPropagation()
0057 {
0058     EventDispatcher eventDispatcher;
0059 
0060     // TODO do everything also with sendNoReply()
0061 
0062     for (int errorAtStep = 0; errorAtStep < StepsCount; errorAtStep++) {
0063 
0064         Connection conn(&eventDispatcher, ConnectAddress::StandardBus::Session);
0065         conn.setDefaultReplyTimeout(500);
0066         conn.waitForConnectionEstablished();
0067         TEST(conn.isConnected());
0068 
0069         ReplierReceiver replier;
0070         conn.setSpontaneousMessageReceiver(&replier);
0071 
0072         Arguments::Writer writer;
0073         writer.beginVariant();
0074 
0075         // If errorAtStep == 0, we do NOT introduce an error, just to check that the intentional
0076         // errors are the only ones
0077 
0078         // The following pattern will repeat for every step where an error can be introduced
0079         if (errorAtStep != 1) {
0080             // Do it right
0081             writer.writeUint32(0);
0082             writer.endVariant();
0083         } else {
0084             // Introduce an error
0085             writer.endVariant(); // a variant may not be empty
0086         }
0087 
0088         if (errorAtStep == 2) {
0089             // too many file descriptors, we "magically" know that the max number of allowed file
0090             // descriptors is 16. TODO it should be possible to ask the Connection about it(?)
0091             for (int i = 0; i < 17; i++) {
0092                 // bogus file descriptors, shouldn't matter: the error should occur before they might
0093                 // possibly need to be valid
0094                 writer.writeUnixFd(100000);
0095             }
0096         }
0097 
0098         Message msg;
0099         if (errorAtStep != 3) {
0100             msg.setType(Message::MethodCallMessage);
0101         }
0102 
0103         // not adding arguments to produce an error won't work - a call without arguments is fine!
0104         msg.setArguments(writer.finish());
0105 
0106         if (errorAtStep != 4) {
0107             msg.setDestination(conn.uniqueName());
0108         }
0109         if (errorAtStep != 5) {
0110             msg.setPath("/foo/bar/dferry/testing");
0111         }
0112         if (errorAtStep != 6) {
0113             msg.setMethod(s_testMethod);
0114         }
0115         // Note interface is optional, so we can't introduce an error by omitting it (except with a signal,
0116         // but we don't test signals)
0117 
0118         if (errorAtStep == 7) {
0119             conn.close();
0120         }
0121 
0122         PendingReply reply = conn.send(std::move(msg));
0123 
0124         if (errorAtStep == 8) {
0125             // Since we haven't sent any (non-internal) messages yet, we rely on the send going through
0126             // immediately, but the receive should fail due to this disconnect.
0127             conn.close();
0128         }
0129 
0130         while (!reply.isFinished()) {
0131             eventDispatcher.poll();
0132         }
0133 
0134 /*
0135 Sources of error yet to do:
0136 Message too large, other untested important Message properties?
0137 Error reply from other side
0138 Timeout
0139 Malformed reply?
0140 Malformed reply arguments?
0141 
0142  */
0143 
0144         static const Error::Code expectedErrors[StepsCount] = {
0145             Error::NoError,
0146             Error::EmptyVariant,
0147             Error::SendingTooManyUnixFds,
0148             Error::MessageType,
0149             Error::NoError, // TODO: probably wrong, message with no destination?!
0150             Error::MessagePath,
0151             Error::MessageMethod,
0152             Error::LocalDisconnect,
0153             Error::LocalDisconnect,
0154             // TODO also test remote disconnect - or is that covered in another test?
0155             Error::NoError // TODO: actually inject an error in that case
0156         };
0157 
0158         std::cerr << "Error at step " << errorAtStep << ": error code = " << reply.error().code()
0159                   << std::endl;
0160         if (reply.reply()) {
0161             std::cerr << "    reply msg error code = " << reply.reply()->error().code()
0162                       << ", reply msg args error code = " << reply.reply()->arguments().error().code()
0163                       << std::endl;
0164         }
0165 
0166         TEST(reply.error().code() == expectedErrors[errorAtStep]);
0167         if (reply.reply()) {
0168             TEST(reply.reply()->error().code() == expectedErrors[errorAtStep]);
0169             TEST(reply.reply()->arguments().error().code() == expectedErrors[errorAtStep]);
0170         }
0171 
0172     }
0173 }
0174 
0175 int main(int, char *[])
0176 {
0177     test_errorPropagation();
0178     std::cout << "Passed!\n";
0179 }