1 /** 2 * @file tests/sdk_test.cpp 3 * @brief Mega SDK test file 4 * 5 * (c) 2015 by Mega Limited, Wellsford, New Zealand 6 * 7 * This file is part of the MEGA SDK - Client Access Engine. 8 * 9 * Applications using the MEGA API must present a valid application key 10 * and comply with the the rules set forth in the Terms of Service. 11 * 12 * The MEGA SDK is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 15 * 16 * @copyright Simplified (2-clause) BSD License. 17 * 18 * You should have received a copy of the license along with this 19 * program. 20 */ 21 22 23 #include "mega.h" 24 #include "../include/megaapi.h" 25 #include "../include/megaapi_impl.h" 26 #include "gtest/gtest.h" 27 #include "test.h" 28 29 #include <iostream> 30 #include <fstream> 31 #include <future> 32 #include <atomic> 33 34 using namespace mega; 35 using ::testing::Test; 36 37 static const string APP_KEY = "8QxzVRxD"; 38 39 // IMPORTANT: the main account must be empty (Cloud & Rubbish) before starting the test and it will be purged at exit. 40 // Both main and auxiliar accounts shouldn't be contacts yet and shouldn't have any pending contact requests. 41 // Set your login credentials as environment variables: $MEGA_EMAIL and $MEGA_PWD (and $MEGA_EMAIL_AUX / $MEGA_PWD_AUX for shares * contacts) 42 43 static const unsigned int pollingT = 500000; // (microseconds) to check if response from server is received 44 static const unsigned int maxTimeout = 600; // Maximum time (seconds) to wait for response from server 45 46 static const string PUBLICFILE = "file.txt"; 47 static const string UPFILE = "file1.txt"; 48 static const string DOWNFILE = "file2.txt"; 49 static const string EMPTYFILE = "empty-file.txt"; 50 static const string AVATARSRC = "logo.png"; 51 static const string AVATARDST = "deleteme.png"; 52 53 54 struct TransferTracker : public ::mega::MegaTransferListener 55 { 56 std::atomic<bool> started = { false }; 57 std::atomic<bool> finished = { false }; 58 std::atomic<int> result = { INT_MAX }; 59 std::promise<int> promiseResult; 60 MegaApi *mApi; 61 TransferTrackerTransferTracker62 TransferTracker(MegaApi *api): mApi(api) 63 { 64 65 } onTransferStartTransferTracker66 void onTransferStart(MegaApi *api, MegaTransfer *transfer) override 67 { 68 started = true; 69 } onTransferFinishTransferTracker70 void onTransferFinish(MegaApi* api, MegaTransfer *transfer, MegaError* error) override 71 { 72 result = error->getErrorCode(); 73 finished = true; 74 promiseResult.set_value(result); 75 } 76 int waitForResult(int seconds = maxTimeout, bool unregisterListenerOnTimeout = true) 77 { 78 auto f = promiseResult.get_future(); 79 if (std::future_status::ready != f.wait_for(std::chrono::seconds(seconds))) 80 { 81 assert(mApi); 82 if (unregisterListenerOnTimeout) 83 { 84 mApi->removeTransferListener(this); 85 } 86 return -999; // local timeout 87 } 88 return f.get(); 89 } 90 }; 91 92 struct RequestTracker : public ::mega::MegaRequestListener 93 { 94 std::atomic<bool> started = { false }; 95 std::atomic<bool> finished = { false }; 96 std::atomic<int> result = { INT_MAX }; 97 std::promise<int> promiseResult; 98 MegaApi *mApi; 99 RequestTrackerRequestTracker100 RequestTracker(MegaApi *api): mApi(api) 101 { 102 103 } onRequestStartRequestTracker104 void onRequestStart(MegaApi* api, MegaRequest *request) override 105 { 106 started = true; 107 } onRequestFinishRequestTracker108 void onRequestFinish(MegaApi* api, MegaRequest *request, MegaError* e) override 109 { 110 result = e->getErrorCode(); 111 finished = true; 112 promiseResult.set_value(result); 113 } 114 int waitForResult(int seconds = maxTimeout, bool unregisterListenerOnTimeout = true) 115 { 116 auto f = promiseResult.get_future(); 117 if (std::future_status::ready != f.wait_for(std::chrono::seconds(seconds))) 118 { 119 assert(mApi); 120 if (unregisterListenerOnTimeout) 121 { 122 mApi->removeRequestListener(this); 123 } 124 return -999; // local timeout 125 } 126 return f.get(); 127 } 128 }; 129 130 // Fixture class with common code for most of tests 131 class SdkTest : public ::testing::Test, public MegaListener, MegaRequestListener, MegaTransferListener, MegaLogger { 132 133 public: 134 135 struct PerApi 136 { 137 MegaApi* megaApi = nullptr; 138 string email; 139 string pwd; 140 int lastError; 141 int lastTransferError; 142 143 // flags to monitor the completion of requests/transfers 144 bool requestFlags[MegaRequest::TOTAL_OF_REQUEST_TYPES]; 145 bool transferFlags[MegaTransfer::TYPE_LOCAL_HTTP_DOWNLOAD]; 146 147 std::unique_ptr<MegaContactRequest> cr; 148 149 // flags to monitor the updates of nodes/users/PCRs due to actionpackets 150 bool nodeUpdated; 151 bool userUpdated; 152 bool contactRequestUpdated; 153 bool accountUpdated; 154 155 MegaHandle h; 156 157 #ifdef ENABLE_CHAT 158 bool chatUpdated; // flags to monitor the updates of chats due to actionpackets 159 map<handle, std::unique_ptr<MegaTextChat>> chats; // runtime cache of fetched/updated chats 160 MegaHandle chatid; // last chat added 161 #endif 162 }; 163 164 std::vector<PerApi> mApi; 165 std::vector<std::unique_ptr<MegaApi>> megaApi; 166 167 // relevant values received in response of requests 168 string link; 169 MegaNode *publicNode; 170 string attributeValue; 171 string sid; 172 std::unique_ptr<MegaStringListMap> stringListMap; 173 std::unique_ptr<MegaStringTable> stringTable; 174 175 m_off_t onTransferUpdate_progress; 176 m_off_t onTransferUpdate_filesize; 177 unsigned onTranferFinishedCount = 0; 178 179 std::mutex lastEventMutex; 180 std::unique_ptr<MegaEvent> lastEvent; 181 182 protected: 183 void SetUp() override; 184 void TearDown() override; 185 186 int getApiIndex(MegaApi* api); 187 188 bool checkAlert(int apiIndex, const string& title, const string& path); 189 bool checkAlert(int apiIndex, const string& title, handle h, int n); 190 onRequestStart(MegaApi * api,MegaRequest * request)191 void onRequestStart(MegaApi *api, MegaRequest *request) override {} onRequestUpdate(MegaApi * api,MegaRequest * request)192 void onRequestUpdate(MegaApi*api, MegaRequest *request) override {} 193 void onRequestFinish(MegaApi *api, MegaRequest *request, MegaError *e) override; onRequestTemporaryError(MegaApi * api,MegaRequest * request,MegaError * error)194 void onRequestTemporaryError(MegaApi *api, MegaRequest *request, MegaError* error) override {} onTransferStart(MegaApi * api,MegaTransfer * transfer)195 void onTransferStart(MegaApi *api, MegaTransfer *transfer) override { } 196 void onTransferFinish(MegaApi* api, MegaTransfer *transfer, MegaError* e) override; 197 void onTransferUpdate(MegaApi *api, MegaTransfer *transfer) override; onTransferTemporaryError(MegaApi * api,MegaTransfer * transfer,MegaError * error)198 void onTransferTemporaryError(MegaApi *api, MegaTransfer *transfer, MegaError* error) override {} 199 void onUsersUpdate(MegaApi* api, MegaUserList *users) override; 200 void onNodesUpdate(MegaApi* api, MegaNodeList *nodes) override; 201 void onAccountUpdate(MegaApi *api) override; 202 void onContactRequestsUpdate(MegaApi* api, MegaContactRequestList* requests) override; onReloadNeeded(MegaApi * api)203 void onReloadNeeded(MegaApi *api) override {} 204 #ifdef ENABLE_SYNC onSyncFileStateChanged(MegaApi * api,MegaSync * sync,string * filePath,int newState)205 void onSyncFileStateChanged(MegaApi *api, MegaSync *sync, string* filePath, int newState) override {} onSyncEvent(MegaApi * api,MegaSync * sync,MegaSyncEvent * event)206 void onSyncEvent(MegaApi *api, MegaSync *sync, MegaSyncEvent *event) override {} onSyncStateChanged(MegaApi * api,MegaSync * sync)207 void onSyncStateChanged(MegaApi *api, MegaSync *sync) override {} onGlobalSyncStateChanged(MegaApi * api)208 void onGlobalSyncStateChanged(MegaApi* api) override {} 209 #endif 210 #ifdef ENABLE_CHAT 211 void onChatsUpdate(MegaApi *api, MegaTextChatList *chats) override; 212 #endif 213 void onEvent(MegaApi* api, MegaEvent *event) override; 214 215 public: 216 void login(unsigned int apiIndex, int timeout = maxTimeout); 217 void loginBySessionId(unsigned int apiIndex, const std::string& sessionId, int timeout = maxTimeout); 218 void fetchnodes(unsigned int apiIndex, int timeout = maxTimeout, bool resumeSyncs = false); 219 void logout(unsigned int apiIndex, int timeout = maxTimeout); 220 char* dumpSession(); 221 void locallogout(int timeout = maxTimeout); 222 void resumeSession(const char *session, int timeout = maxTimeout); 223 224 void purgeTree(MegaNode *p, bool depthfirst = true); 225 bool waitForResponse(bool *responseReceived, unsigned int timeout = maxTimeout); 226 227 bool synchronousRequest(unsigned apiIndex, int type, std::function<void()> f, unsigned int timeout = maxTimeout); 228 bool synchronousTransfer(unsigned apiIndex, int type, std::function<void()> f, unsigned int timeout = maxTimeout); 229 230 // convenience functions - template args just make it easy to code, no need to copy all the exact argument types with listener defaults etc. To add a new one, just copy a line and change the flag and the function called. synchronousStartUpload(unsigned apiIndex,Args...args)231 template<typename ... Args> int synchronousStartUpload(unsigned apiIndex, Args... args) { synchronousTransfer(apiIndex, MegaTransfer::TYPE_UPLOAD, [this, apiIndex, args...]() { megaApi[apiIndex]->startUpload(args...); }); return mApi[apiIndex].lastError; } synchronousStartDownload(unsigned apiIndex,Args...args)232 template<typename ... Args> int synchronousStartDownload(unsigned apiIndex, Args... args) { synchronousTransfer(apiIndex, MegaTransfer::TYPE_DOWNLOAD, [this, apiIndex, args...]() { megaApi[apiIndex]->startDownload(args...); }); return mApi[apiIndex].lastTransferError; } synchronousCatchup(unsigned apiIndex,Args...args)233 template<typename ... Args> int synchronousCatchup(unsigned apiIndex, Args... args) { synchronousRequest(apiIndex, MegaRequest::TYPE_CATCHUP, [this, apiIndex, args...]() { megaApi[apiIndex]->catchup(args...); }); return mApi[apiIndex].lastError; } synchronousCreateAccount(unsigned apiIndex,Args...args)234 template<typename ... Args> int synchronousCreateAccount(unsigned apiIndex, Args... args) { synchronousRequest(apiIndex, MegaRequest::TYPE_CREATE_ACCOUNT, [this, apiIndex, args...]() { megaApi[apiIndex]->createAccount(args...); }); return mApi[apiIndex].lastError; } synchronousResumeCreateAccount(unsigned apiIndex,Args...args)235 template<typename ... Args> int synchronousResumeCreateAccount(unsigned apiIndex, Args... args) { synchronousRequest(apiIndex, MegaRequest::TYPE_CREATE_ACCOUNT, [this, apiIndex, args...]() { megaApi[apiIndex]->resumeCreateAccount(args...); }); return mApi[apiIndex].lastError; } synchronousSendSignupLink(unsigned apiIndex,Args...args)236 template<typename ... Args> int synchronousSendSignupLink(unsigned apiIndex, Args... args) { synchronousRequest(apiIndex, MegaRequest::TYPE_SEND_SIGNUP_LINK, [this, apiIndex, args...]() { megaApi[apiIndex]->sendSignupLink(args...); }); return mApi[apiIndex].lastError; } synchronousFastLogin(unsigned apiIndex,Args...args)237 template<typename ... Args> int synchronousFastLogin(unsigned apiIndex, Args... args) { synchronousRequest(apiIndex, MegaRequest::TYPE_LOGIN, [this, apiIndex, args...]() { megaApi[apiIndex]->fastLogin(args...); }); return mApi[apiIndex].lastError; } synchronousRemove(unsigned apiIndex,Args...args)238 template<typename ... Args> int synchronousRemove(unsigned apiIndex, Args... args) { synchronousRequest(apiIndex, MegaRequest::TYPE_REMOVE, [this, apiIndex, args...]() { megaApi[apiIndex]->remove(args...); }); return mApi[apiIndex].lastError; } synchronousInviteContact(unsigned apiIndex,Args...args)239 template<typename ... Args> int synchronousInviteContact(unsigned apiIndex, Args... args) { synchronousRequest(apiIndex, MegaRequest::TYPE_INVITE_CONTACT, [this, apiIndex, args...]() { megaApi[apiIndex]->inviteContact(args...); }); return mApi[apiIndex].lastError; } synchronousReplyContactRequest(unsigned apiIndex,Args...args)240 template<typename ... Args> int synchronousReplyContactRequest(unsigned apiIndex, Args... args) { synchronousRequest(apiIndex, MegaRequest::TYPE_REPLY_CONTACT_REQUEST, [this, apiIndex, args...]() { megaApi[apiIndex]->replyContactRequest(args...); }); return mApi[apiIndex].lastError; } synchronousRemoveContact(unsigned apiIndex,Args...args)241 template<typename ... Args> int synchronousRemoveContact(unsigned apiIndex, Args... args) { synchronousRequest(apiIndex, MegaRequest::TYPE_REMOVE_CONTACT, [this, apiIndex, args...]() { megaApi[apiIndex]->removeContact(args...); }); return mApi[apiIndex].lastError; } synchronousShare(unsigned apiIndex,Args...args)242 template<typename ... Args> int synchronousShare(unsigned apiIndex, Args... args) { synchronousRequest(apiIndex, MegaRequest::TYPE_SHARE, [this, apiIndex, args...]() { megaApi[apiIndex]->share(args...); }); return mApi[apiIndex].lastError; } synchronousExportNode(unsigned apiIndex,Args...args)243 template<typename ... Args> int synchronousExportNode(unsigned apiIndex, Args... args) { synchronousRequest(apiIndex, MegaRequest::TYPE_EXPORT, [this, apiIndex, args...]() { megaApi[apiIndex]->exportNode(args...); }); return mApi[apiIndex].lastError; } synchronousGetRegisteredContacts(unsigned apiIndex,Args...args)244 template<typename ... Args> int synchronousGetRegisteredContacts(unsigned apiIndex, Args... args) { synchronousRequest(apiIndex, MegaRequest::TYPE_GET_REGISTERED_CONTACTS, [this, apiIndex, args...]() { megaApi[apiIndex]->getRegisteredContacts(args...); }); return mApi[apiIndex].lastError; } synchronousGetCountryCallingCodes(unsigned apiIndex,Args...args)245 template<typename ... Args> int synchronousGetCountryCallingCodes(unsigned apiIndex, Args... args) { synchronousRequest(apiIndex, MegaRequest::TYPE_GET_COUNTRY_CALLING_CODES, [this, apiIndex, args...]() { megaApi[apiIndex]->getCountryCallingCodes(args...); }); return mApi[apiIndex].lastError; } synchronousGetUserAvatar(unsigned apiIndex,Args...args)246 template<typename ... Args> int synchronousGetUserAvatar(unsigned apiIndex, Args... args) { synchronousRequest(apiIndex, MegaRequest::TYPE_GET_ATTR_USER, [this, apiIndex, args...]() { megaApi[apiIndex]->getUserAvatar(args...); }); return mApi[apiIndex].lastError; } synchronousGetUserAttribute(unsigned apiIndex,Args...args)247 template<typename ... Args> int synchronousGetUserAttribute(unsigned apiIndex, Args... args) { synchronousRequest(apiIndex, MegaRequest::TYPE_GET_ATTR_USER, [this, apiIndex, args...]() { megaApi[apiIndex]->getUserAttribute(args...); }); return mApi[apiIndex].lastError; } synchronousSetNodeDuration(unsigned apiIndex,Args...args)248 template<typename ... Args> int synchronousSetNodeDuration(unsigned apiIndex, Args... args) { synchronousRequest(apiIndex, MegaRequest::TYPE_SET_ATTR_NODE, [this, apiIndex, args...]() { megaApi[apiIndex]->setNodeDuration(args...); }); return mApi[apiIndex].lastError; } synchronousSetNodeCoordinates(unsigned apiIndex,Args...args)249 template<typename ... Args> int synchronousSetNodeCoordinates(unsigned apiIndex, Args... args) { synchronousRequest(apiIndex, MegaRequest::TYPE_SET_ATTR_NODE, [this, apiIndex, args...]() { megaApi[apiIndex]->setNodeCoordinates(args...); }); return mApi[apiIndex].lastError; } 250 251 252 // convenience functions - make a request and wait for the result via listener, return the result code. To add new functions to call, just copy the line doRequestLogout(unsigned apiIndex,requestArgs...args)253 template<typename ... requestArgs> int doRequestLogout(unsigned apiIndex, requestArgs... args) { RequestTracker rt(megaApi[apiIndex].get()); megaApi[apiIndex]->logout(args..., &rt); return rt.waitForResult(); } 254 255 void createFile(string filename, bool largeFile = true); 256 int64_t getFilesize(string filename); 257 void deleteFile(string filename); 258 259 void getMegaApiAux(unsigned index = 1); 260 void releaseMegaApi(unsigned int apiIndex); 261 262 void inviteContact(string email, string message, int action); 263 void replyContact(MegaContactRequest *cr, int action); 264 void removeContact(string email, int timeout = maxTimeout); 265 void setUserAttribute(int type, string value, int timeout = maxTimeout); 266 void getUserAttribute(MegaUser *u, int type, int timeout = maxTimeout, int accountIndex = 1); 267 268 void shareFolder(MegaNode *n, const char *email, int action, int timeout = maxTimeout); 269 270 void createPublicLink(unsigned apiIndex, MegaNode *n, m_time_t expireDate = 0, int timeout = maxTimeout); 271 void importPublicLink(unsigned apiIndex, string link, MegaNode *parent, int timeout = maxTimeout); 272 void getPublicNode(unsigned apiIndex, string link, int timeout = maxTimeout); 273 void removePublicLink(unsigned apiIndex, MegaNode *n, int timeout = maxTimeout); 274 275 void getContactRequest(unsigned int apiIndex, bool outgoing, int expectedSize = 1); 276 277 void createFolder(unsigned int apiIndex, const char * name, MegaNode *n, int timeout = maxTimeout); 278 279 void getRegisteredContacts(const std::map<std::string, std::string>& contacts); 280 281 void getCountryCallingCodes(int timeout = maxTimeout); 282 283 #ifdef ENABLE_CHAT 284 void createChat(bool group, MegaTextChatPeerList *peers, int timeout = maxTimeout); 285 #endif 286 }; 287