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