1 /**
2  * (c) 2019 by Mega Limited, Wellsford, New Zealand
3  *
4  * This file is part of the MEGA SDK - Client Access Engine.
5  *
6  * Applications using the MEGA API must present a valid application key
7  * and comply with the the rules set forth in the Terms of Service.
8  *
9  * The MEGA SDK is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12  *
13  * @copyright Simplified (2-clause) BSD License.
14  *
15  * You should have received a copy of the license along with this
16  * program.
17  */
18 #include <gtest/gtest.h>
19 
20 #include <mega/logging.h>
21 
22 #ifdef NOT_REALLY_NEEDED_BECAUSE_WE_EXERCISE_IT_ALL_THE_TIME_ANYWAY
23 
24 #ifdef ENABLE_LOG_PERFORMANCE
25 namespace {
26 
27 class MockLogger : public mega::Logger
28 {
29 public:
30 
MockLogger()31     MockLogger()
32     {
33         mega::SimpleLogger::logger = this;
34     }
35 
~MockLogger()36     ~MockLogger()
37     {
38         mega::SimpleLogger::logger = nullptr;
39     }
40 
log(const char * time,int loglevel,const char * source,const char * message)41     void log(const char *time, int loglevel, const char *source, const char *message) override
42     {
43         EXPECT_EQ(nullptr, time);
44         EXPECT_EQ(nullptr, source);
45         EXPECT_NE(nullptr, message);
46         mLogLevel.insert(loglevel);
47         mMessage.push_back(message);
48     }
49 
checkLogLevel(const int expLogLevel) const50     void checkLogLevel(const int expLogLevel) const
51     {
52         EXPECT_EQ(1, mLogLevel.size());
53         EXPECT_EQ(expLogLevel, *mLogLevel.begin());
54     }
55 
56     std::vector<std::string> mMessage;
57 
58 private:
59     std::set<int> mLogLevel;
60 };
61 
expMsg(const std::string & file,const int line,const std::string & message)62 std::string expMsg(const std::string& file, const int line, const std::string& message)
63 {
64     return message + " ["+file + ":" + std::to_string(line) + "]";
65 }
66 
67 #ifdef _WIN32
expWMsg(const std::string & file,const int line,const std::wstring & message)68 std::string expWMsg(const std::string& file, const int line, const std::wstring& message)
69 {
70     return file + ":" + std::to_string(line) + " " + std::string(message.begin(), message.end());
71 }
72 #endif
73 
74 }
75 
TEST(Logging,performanceMode_forStdString)76 TEST(Logging, performanceMode_forStdString)
77 {
78     for (int level = 0; level <= mega::LogLevel::logMax; ++level)
79     {
80         MockLogger logger;
81         const std::string file = "file.cpp";
82         const int line = 13;
83         const std::string message = "some message";
84         mega::SimpleLogger{static_cast<mega::LogLevel>(level), file.c_str(), line} << message;
85         logger.checkLogLevel(level);
86         ASSERT_EQ(1, logger.mMessage.size());
87         ASSERT_EQ(expMsg(file, line, message), logger.mMessage[0]);
88     }
89 }
90 
91 #ifdef _WIN32
TEST(Logging,performanceMode_forWStdString)92 TEST(Logging, performanceMode_forWStdString)
93 {
94     for (int level = 0; level <= mega::LogLevel::logMax; ++level)
95     {
96         MockLogger logger;
97         const std::string file = "file.cpp";
98         const int line = 13;
99         const std::wstring message = L"\u039C\u03C5\u03C4\u03B9\u03BB\u03B7\u03BD\u03B1\u03AF\u03BF\u03C2\20\u0391\u03B2\u03C1\u03AC\u03C2";
100         mega::SimpleLogger{static_cast<mega::LogLevel>(level), file.c_str(), line} << message;
101         logger.checkLogLevel(level);
102         ASSERT_EQ(1, logger.mMessage.size());
103         ASSERT_EQ(expWMsg(file, line, message), logger.mMessage[0]);
104     }
105 }
106 #endif
107 
TEST(Logging,performanceMode_forCString)108 TEST(Logging, performanceMode_forCString)
109 {
110     for (int level = 0; level <= mega::LogLevel::logMax; ++level)
111     {
112         MockLogger logger;
113         const std::string file = "file.cpp";
114         const int line = 13;
115         const std::string message = "some message";
116         mega::SimpleLogger{static_cast<mega::LogLevel>(level), file.c_str(), line} << message.c_str();
117         logger.checkLogLevel(level);
118         ASSERT_EQ(1, logger.mMessage.size());
119         ASSERT_EQ(expMsg(file, line, message), logger.mMessage[0]);
120     }
121 }
122 
TEST(Logging,performanceMode_forEnum)123 TEST(Logging, performanceMode_forEnum)
124 {
125     for (int level = 0; level <= mega::LogLevel::logMax; ++level)
126     {
127         MockLogger logger;
128         const std::string file = "file.cpp";
129         const int line = 13;
130         const auto obj = mega::LogLevel::logDebug;
131         mega::SimpleLogger{static_cast<mega::LogLevel>(level), file.c_str(), line} << obj;
132         logger.checkLogLevel(level);
133         ASSERT_EQ(1, logger.mMessage.size());
134         ASSERT_EQ(expMsg(file, line, "4"), logger.mMessage[0]);
135     }
136 }
137 
TEST(Logging,performanceMode_forPointer)138 TEST(Logging, performanceMode_forPointer)
139 {
140     for (int level = 0; level <= mega::LogLevel::logMax; ++level)
141     {
142         MockLogger logger;
143         const std::string file = "file.cpp";
144         const int line = 13;
145         const double obj = 42;
146         mega::SimpleLogger{static_cast<mega::LogLevel>(level), file.c_str(), line} << &obj;
147         logger.checkLogLevel(level);
148         ASSERT_EQ(1, logger.mMessage.size());
149         ASSERT_GE(logger.mMessage[0].size(), file.size() + 5); // 5 = ':13 ' plus null terminator
150     }
151 }
152 
TEST(Logging,performanceMode_forNullPointer)153 TEST(Logging, performanceMode_forNullPointer)
154 {
155     for (int level = 0; level <= mega::LogLevel::logMax; ++level)
156     {
157         MockLogger logger;
158         const std::string file = "file.cpp";
159         const int line = 13;
160         const double* obj = nullptr;
161         mega::SimpleLogger{static_cast<mega::LogLevel>(level), file.c_str(), line} << obj;
162         logger.checkLogLevel(level);
163         ASSERT_EQ(1, logger.mMessage.size());
164         ASSERT_EQ(expMsg(file, line, "(NULL)"), logger.mMessage[0]);
165     }
166 }
167 
168 namespace {
169 
170 template<typename Type>
test_forIntegerNumber(const Type number)171 void test_forIntegerNumber(const Type number)
172 {
173     for (int level = 0; level <= mega::LogLevel::logMax; ++level)
174     {
175         MockLogger logger;
176         const std::string file = "file.cpp";
177         const int line = 13;
178         mega::SimpleLogger{static_cast<mega::LogLevel>(level), file.c_str(), line} << number;
179         logger.checkLogLevel(level);
180         EXPECT_EQ(1, logger.mMessage.size());
181         std::ostringstream expected;
182         expected << number;
183         EXPECT_EQ(expMsg(file, line, expected.str()), logger.mMessage[0]);
184     }
185 }
186 
187 template<typename Type>
test_forFloatingNumber(const Type number)188 void test_forFloatingNumber(const Type number)
189 {
190     for (int level = 0; level <= mega::LogLevel::logMax; ++level)
191     {
192         MockLogger logger;
193         const std::string file = "file.cpp";
194         const int line = 13;
195         mega::SimpleLogger{static_cast<mega::LogLevel>(level), file.c_str(), line} << number;
196         logger.checkLogLevel(level);
197         EXPECT_EQ(1, logger.mMessage.size());
198         std::ostringstream expected;
199         expected << number;
200         const auto msg = expMsg(file, line, expected.str());
201         EXPECT_NE(logger.mMessage[0].find(msg), std::string::npos);
202     }
203 }
204 
205 }
206 
TEST(Logging,performanceMode_forInt)207 TEST(Logging, performanceMode_forInt)
208 {
209     test_forIntegerNumber<int>(0);
210     test_forIntegerNumber<int>(42);
211     test_forIntegerNumber<int>(-42);
212     test_forIntegerNumber<int>(std::numeric_limits<int>::lowest());
213     test_forIntegerNumber<int>(std::numeric_limits<int>::max());
214 }
215 
TEST(Logging,performanceMode_forLong)216 TEST(Logging, performanceMode_forLong)
217 {
218     test_forIntegerNumber<long>(0);
219     test_forIntegerNumber<long>(42);
220     test_forIntegerNumber<long>(-42);
221     test_forIntegerNumber<long>(std::numeric_limits<long>::lowest());
222     test_forIntegerNumber<long>(std::numeric_limits<long>::max());
223 }
224 
TEST(Logging,performanceMode_forLongLong)225 TEST(Logging, performanceMode_forLongLong)
226 {
227     test_forIntegerNumber<long long>(0);
228     test_forIntegerNumber<long long>(42);
229     test_forIntegerNumber<long long>(-42);
230     test_forIntegerNumber<long long>(std::numeric_limits<long long>::lowest());
231     test_forIntegerNumber<long long>(std::numeric_limits<long long>::max());
232 }
233 
TEST(Logging,performanceMode_forUnsignedInt)234 TEST(Logging, performanceMode_forUnsignedInt)
235 {
236     test_forIntegerNumber<unsigned int>(0);
237     test_forIntegerNumber<unsigned int>(42);
238     test_forIntegerNumber<unsigned int>(std::numeric_limits<unsigned int>::lowest());
239     test_forIntegerNumber<unsigned int>(std::numeric_limits<unsigned int>::max());
240 }
241 
TEST(Logging,performanceMode_forUnsignedLong)242 TEST(Logging, performanceMode_forUnsignedLong)
243 {
244     test_forIntegerNumber<unsigned long>(0);
245     test_forIntegerNumber<unsigned long>(42);
246     test_forIntegerNumber<unsigned long>(std::numeric_limits<unsigned long>::lowest());
247     test_forIntegerNumber<unsigned long>(std::numeric_limits<unsigned long>::max());
248 }
249 
TEST(Logging,performanceMode_forUnsignedLongLong)250 TEST(Logging, performanceMode_forUnsignedLongLong)
251 {
252     test_forIntegerNumber<unsigned long long>(0);
253     test_forIntegerNumber<unsigned long long>(42);
254     test_forIntegerNumber<unsigned long long>(std::numeric_limits<unsigned long long>::lowest());
255     test_forIntegerNumber<unsigned long long>(std::numeric_limits<unsigned long long>::max());
256 }
257 
TEST(Logging,performanceMode_forFloat)258 TEST(Logging, performanceMode_forFloat)
259 {
260     test_forFloatingNumber<float>(0.f);
261     test_forFloatingNumber<float>(42.123f);
262     test_forFloatingNumber<float>(-42.123f);
263     test_forFloatingNumber<float>(std::numeric_limits<float>::lowest());
264     test_forFloatingNumber<float>(std::numeric_limits<float>::min());
265     test_forFloatingNumber<float>(std::numeric_limits<float>::max());
266 }
267 
TEST(Logging,performanceMode_forDouble)268 TEST(Logging, performanceMode_forDouble)
269 {
270     test_forFloatingNumber<double>(0.);
271     test_forFloatingNumber<double>(42.123);
272     test_forFloatingNumber<double>(-42.123);
273     test_forFloatingNumber<double>(std::numeric_limits<double>::lowest());
274     test_forFloatingNumber<double>(std::numeric_limits<double>::min());
275     test_forFloatingNumber<double>(std::numeric_limits<double>::max());
276 }
277 
TEST(Logging,performanceMode_withMessageLargeThanLogBuffer)278 TEST(Logging, performanceMode_withMessageLargeThanLogBuffer)
279 {
280     for (int level = 0; level <= mega::LogLevel::logMax; ++level)
281     {
282         MockLogger logger;
283         const std::string file = "file.cpp";
284         const int line = 13;
285         const std::string firstMessage(256 - file.size() - 5, 'X'); // 5 = ':13 ' plus null terminator
286         const std::string secondMessage = "yay";
287         const std::string message = firstMessage + secondMessage;
288         mega::SimpleLogger{static_cast<mega::LogLevel>(level), file.c_str(), line} << message;
289         logger.checkLogLevel(level);
290         ASSERT_EQ(2, logger.mMessage.size());
291         ASSERT_EQ(expMsg(file, line, message).substr(0,255), logger.mMessage[0]);
292         ASSERT_EQ(expMsg(file, line, message).substr(255), logger.mMessage[1]);
293     }
294 }
295 
TEST(Logging,performanceMode_withHugeMessage)296 TEST(Logging, performanceMode_withHugeMessage)
297 {
298     for (int level = 0; level <= mega::LogLevel::logMax; ++level)
299     {
300         MockLogger logger;
301         const std::string file = "file.cpp";
302         const int line = 13;
303         const std::string message(5000, 'X');
304         mega::SimpleLogger{static_cast<mega::LogLevel>(level), file.c_str(), line} << message;
305         logger.checkLogLevel(level);
306 
307         const int totallength = 5000 + strlen(" [file.cpp:13]") + 1;
308         const size_t fullMsgCount = totallength / 255;
309         ASSERT_EQ(fullMsgCount + 1, logger.mMessage.size());
310         ASSERT_EQ(totallength % 255 - 1, logger.mMessage.back().size());
311     }
312 }
313 
TEST(Logging,performanceMode_withHugeMessage_butNoLogger)314 TEST(Logging, performanceMode_withHugeMessage_butNoLogger)
315 {
316     for (int level = 0; level <= mega::LogLevel::logMax; ++level)
317     {
318         mega::SimpleLogger::logger = nullptr;
319         const std::string file = "file.cpp";
320         const int line = 13;
321         const std::string message(5000, 'X');
322         mega::SimpleLogger{static_cast<mega::LogLevel>(level), file.c_str(), line} << message;
323         // ensure no crash or other funny business
324     }
325 }
326 
327 #else
328 
329 namespace {
330 
331 class MockLogger : public mega::Logger
332 {
333 public:
334 
MockLogger()335     MockLogger()
336     {
337         mega::SimpleLogger::logger = this;
338     }
339 
~MockLogger()340     ~MockLogger()
341     {
342         mega::SimpleLogger::logger = nullptr;
343     }
344 
log(const char * time,int loglevel,const char * source,const char * message)345     void log(const char *time, int loglevel, const char *source, const char *message) override
346     {
347         EXPECT_NE(nullptr, time);
348         EXPECT_NE(nullptr, source);
349         EXPECT_NE(nullptr, message);
350         mLogLevel.insert(loglevel);
351         mMessage.push_back(message);
352     }
353 
checkLogLevel(const int expLogLevel) const354     void checkLogLevel(const int expLogLevel) const
355     {
356         EXPECT_EQ(1u, mLogLevel.size());
357         EXPECT_EQ(expLogLevel, *mLogLevel.begin());
358     }
359 
360     std::vector<std::string> mMessage;
361 
362 private:
363     std::set<int> mLogLevel;
364 };
365 
366 }
367 
368 #endif
369 
TEST(Logging,toStr)370 TEST(Logging, toStr)
371 {
372     ASSERT_EQ(0, std::strcmp("verbose", mega::SimpleLogger::toStr(mega::LogLevel::logMax)));
373     ASSERT_EQ(0, std::strcmp("debug", mega::SimpleLogger::toStr(mega::LogLevel::logDebug)));
374     ASSERT_EQ(0, std::strcmp("info", mega::SimpleLogger::toStr(mega::LogLevel::logInfo)));
375     ASSERT_EQ(0, std::strcmp("warn", mega::SimpleLogger::toStr(mega::LogLevel::logWarning)));
376     ASSERT_EQ(0, std::strcmp("err", mega::SimpleLogger::toStr(mega::LogLevel::logError)));
377     ASSERT_EQ(0, std::strcmp("FATAL", mega::SimpleLogger::toStr(mega::LogLevel::logFatal)));
378 }
379 
380 #ifdef NDEBUG
TEST(Logging,toStr_withBadLogLevel)381 TEST(Logging, toStr_withBadLogLevel)
382 {
383     ASSERT_EQ(0, std::strcmp("", mega::SimpleLogger::toStr(static_cast<mega::LogLevel>(42))));
384 }
385 #endif
386 
TEST(Logging,macroVerbose)387 TEST(Logging, macroVerbose)
388 {
389     for (int level = 0; level <= mega::LogLevel::logMax; ++level)
390     {
391         MockLogger logger;
392         mega::SimpleLogger::setLogLevel(static_cast<mega::LogLevel>(level));
393         const std::string msg = "foobar";
394         LOG_verbose << msg;
395         const auto currentLevel = mega::LogLevel::logMax;
396         if (level >= currentLevel)
397         {
398             logger.checkLogLevel(currentLevel);
399             ASSERT_EQ(1u, logger.mMessage.size());
400             EXPECT_NE(logger.mMessage[0].find(msg), std::string::npos);
401         }
402         else
403         {
404             ASSERT_EQ(0u, logger.mMessage.size());
405         }
406     }
407 }
408 
TEST(Logging,macroDebug)409 TEST(Logging, macroDebug)
410 {
411     for (int level = 0; level <= mega::LogLevel::logMax; ++level)
412     {
413         MockLogger logger;
414         mega::SimpleLogger::setLogLevel(static_cast<mega::LogLevel>(level));
415         const std::string msg = "foobar";
416         LOG_debug << msg;
417         const auto currentLevel = mega::LogLevel::logDebug;
418         if (level >= currentLevel)
419         {
420             logger.checkLogLevel(currentLevel);
421             ASSERT_EQ(1u, logger.mMessage.size());
422             EXPECT_NE(logger.mMessage[0].find(msg), std::string::npos);
423         }
424         else
425         {
426             ASSERT_EQ(0u, logger.mMessage.size());
427         }
428     }
429 }
430 
TEST(Logging,macroInfo)431 TEST(Logging, macroInfo)
432 {
433     for (int level = 0; level <= mega::LogLevel::logMax; ++level)
434     {
435         MockLogger logger;
436         mega::SimpleLogger::setLogLevel(static_cast<mega::LogLevel>(level));
437         const std::string msg = "foobar";
438         LOG_info << msg;
439         const auto currentLevel = mega::LogLevel::logInfo;
440         if (level >= currentLevel)
441         {
442             logger.checkLogLevel(currentLevel);
443             ASSERT_EQ(1u, logger.mMessage.size());
444             EXPECT_NE(logger.mMessage[0].find(msg), std::string::npos);
445         }
446         else
447         {
448             ASSERT_EQ(0u, logger.mMessage.size());
449         }
450     }
451 }
452 
TEST(Logging,macroWarn)453 TEST(Logging, macroWarn)
454 {
455     for (int level = 0; level <= mega::LogLevel::logMax; ++level)
456     {
457         MockLogger logger;
458         mega::SimpleLogger::setLogLevel(static_cast<mega::LogLevel>(level));
459         const std::string msg = "foobar";
460         LOG_warn << msg;
461         const auto currentLevel = mega::LogLevel::logWarning;
462         if (level >= currentLevel)
463         {
464             logger.checkLogLevel(currentLevel);
465             ASSERT_EQ(1u, logger.mMessage.size());
466             EXPECT_NE(logger.mMessage[0].find(msg), std::string::npos);
467         }
468         else
469         {
470             ASSERT_EQ(0u, logger.mMessage.size());
471         }
472     }
473 }
474 
TEST(Logging,macroErr)475 TEST(Logging, macroErr)
476 {
477     for (int level = 0; level <= mega::LogLevel::logMax; ++level)
478     {
479         MockLogger logger;
480         mega::SimpleLogger::setLogLevel(static_cast<mega::LogLevel>(level));
481         const std::string msg = "foobar";
482         LOG_err << msg;
483         const auto currentLevel = mega::LogLevel::logError;
484         if (level >= currentLevel)
485         {
486             logger.checkLogLevel(currentLevel);
487             ASSERT_EQ(1u, logger.mMessage.size());
488             EXPECT_NE(logger.mMessage[0].find(msg), std::string::npos);
489         }
490         else
491         {
492             ASSERT_EQ(0u, logger.mMessage.size());
493         }
494     }
495 }
496 
TEST(Logging,macroFatal)497 TEST(Logging, macroFatal)
498 {
499     for (int level = 0; level <= mega::LogLevel::logMax; ++level)
500     {
501         MockLogger logger;
502         mega::SimpleLogger::setLogLevel(static_cast<mega::LogLevel>(level));
503         const std::string msg = "foobar";
504         LOG_fatal << msg;
505         logger.checkLogLevel(mega::LogLevel::logFatal);
506         ASSERT_EQ(1u, logger.mMessage.size());
507         EXPECT_NE(logger.mMessage[0].find(msg), std::string::npos);
508     }
509 }
510 
511 #endif
512