00001
00033 #include "linden_common.h"
00034 #include "llerror.h"
00035
00036 namespace
00037 {
00038 void test_that_error_h_includes_enough_things_to_compile_a_message()
00039 {
00040 llinfos << "!" << llendl;
00041 }
00042 }
00043
00044 #include <tut/tut.h>
00045 #include "lltut.h"
00046
00047 #include <vector>
00048
00049 #include "llerrorcontrol.h"
00050 #include "llsd.h"
00051
00052 namespace
00053 {
00054 static bool fatalWasCalled;
00055 void fatalCall(const std::string&) { fatalWasCalled = true; }
00056
00057 class TestRecorder : public LLError::Recorder
00058 {
00059 public:
00060 TestRecorder() : mWantsTime(false) { }
00061 ~TestRecorder() { LLError::removeRecorder(this); }
00062
00063 void recordMessage(LLError::ELevel level,
00064 const std::string& message)
00065 {
00066 mMessages.push_back(message);
00067 }
00068
00069 int countMessages() { return (int) mMessages.size(); }
00070 void clearMessages() { mMessages.clear(); }
00071
00072 void setWantsTime(bool t) { mWantsTime = t; }
00073 bool wantsTime() { return mWantsTime; }
00074
00075 std::string message(int n)
00076 {
00077 std::ostringstream test_name;
00078 test_name << "testing message " << n << ", not enough messages";
00079
00080 tut::ensure(test_name.str(), n < countMessages());
00081 return mMessages[n];
00082 }
00083
00084 private:
00085 typedef std::vector<std::string> MessageVector;
00086 MessageVector mMessages;
00087
00088 bool mWantsTime;
00089 };
00090 }
00091
00092 namespace tut
00093 {
00094 struct ErrorTestData
00095 {
00096 TestRecorder mRecorder;
00097 LLError::Settings* mPriorErrorSettings;
00098
00099 ErrorTestData()
00100 {
00101 fatalWasCalled = false;
00102
00103 mPriorErrorSettings = LLError::saveAndResetSettings();
00104 LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
00105 LLError::setFatalFunction(fatalCall);
00106 LLError::addRecorder(&mRecorder);
00107 }
00108
00109 ~ErrorTestData()
00110 {
00111 LLError::removeRecorder(&mRecorder);
00112 LLError::restoreSettings(mPriorErrorSettings);
00113 }
00114
00115 void ensure_message_count(int expectedCount)
00116 {
00117 ensure_equals("message count", mRecorder.countMessages(), expectedCount);
00118 }
00119
00120 void ensure_message_contains(int n, const std::string& expectedText)
00121 {
00122 std::ostringstream test_name;
00123 test_name << "testing message " << n;
00124
00125 ensure_contains(test_name.str(), mRecorder.message(n), expectedText);
00126 }
00127
00128 void ensure_message_does_not_contain(int n, const std::string& expectedText)
00129 {
00130 std::ostringstream test_name;
00131 test_name << "testing message " << n;
00132
00133 ensure_does_not_contain(test_name.str(), mRecorder.message(n), expectedText);
00134 }
00135 };
00136
00137 typedef test_group<ErrorTestData> ErrorTestGroup;
00138 typedef ErrorTestGroup::object ErrorTestObject;
00139
00140 ErrorTestGroup errorTestGroup("error");
00141
00142 template<> template<>
00143 void ErrorTestObject::test<1>()
00144
00145 {
00146 llinfos << "test" << llendl;
00147 llinfos << "bob" << llendl;
00148
00149 ensure_message_contains(0, "test");
00150 ensure_message_contains(1, "bob");
00151 }
00152 }
00153
00154 namespace
00155 {
00156 void writeSome()
00157 {
00158 lldebugs << "one" << llendl;
00159 llinfos << "two" << llendl;
00160 llwarns << "three" << llendl;
00161 llerrs << "four" << llendl;
00162
00163 }
00164 };
00165
00166 namespace tut
00167 {
00168 template<> template<>
00169 void ErrorTestObject::test<2>()
00170
00171 {
00172 LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
00173 writeSome();
00174 ensure_message_contains(0, "one");
00175 ensure_message_contains(1, "two");
00176 ensure_message_contains(2, "three");
00177 ensure_message_contains(3, "error");
00178 ensure_message_contains(4, "four");
00179 ensure_message_count(5);
00180
00181 LLError::setDefaultLevel(LLError::LEVEL_INFO);
00182 writeSome();
00183 ensure_message_contains(5, "two");
00184 ensure_message_contains(6, "three");
00185 ensure_message_contains(7, "error");
00186 ensure_message_contains(8, "four");
00187 ensure_message_count(9);
00188
00189 LLError::setDefaultLevel(LLError::LEVEL_WARN);
00190 writeSome();
00191 ensure_message_contains(9, "three");
00192 ensure_message_contains(10, "error");
00193 ensure_message_contains(11, "four");
00194 ensure_message_count(12);
00195
00196 LLError::setDefaultLevel(LLError::LEVEL_ERROR);
00197 writeSome();
00198 ensure_message_contains(12, "error");
00199 ensure_message_contains(13, "four");
00200 ensure_message_count(14);
00201
00202 LLError::setDefaultLevel(LLError::LEVEL_NONE);
00203 writeSome();
00204 ensure_message_count(14);
00205 }
00206
00207 template<> template<>
00208 void ErrorTestObject::test<3>()
00209
00210 {
00211 writeSome();
00212 ensure_message_contains(0, "DEBUG: ");
00213 ensure_message_contains(1, "INFO: ");
00214 ensure_message_contains(2, "WARNING: ");
00215 ensure_message_does_not_contain(3, "ERROR");
00216 ensure_message_contains(4, "ERROR: ");
00217 ensure_message_count(5);
00218 }
00219
00220 template<> template<>
00221 void ErrorTestObject::test<4>()
00222
00223 {
00224 std::string thisFile = __FILE__;
00225 std::string abbreviateFile = LLError::abbreviateFile(thisFile);
00226
00227 ensure_ends_with("file name abbreviation",
00228 abbreviateFile,
00229 "test/llerror_tut.cpp"
00230 );
00231 ensure_does_not_contain("file name abbreviation",
00232 abbreviateFile, "indra");
00233
00234 std::string someFile =
00235 #if LL_WINDOWS
00236 "C:/amy/bob/cam.cpp"
00237 #else
00238 "/amy/bob/cam.cpp"
00239 #endif
00240 ;
00241 std::string someAbbreviation = LLError::abbreviateFile(someFile);
00242
00243 ensure_equals("non-indra file abbreviation",
00244 someAbbreviation, someFile);
00245 }
00246 }
00247
00248 namespace
00249 {
00250 std::string locationString(int line)
00251 {
00252 std::ostringstream location;
00253 location << LLError::abbreviateFile(__FILE__)
00254 << "(" << line << ") : ";
00255
00256 return location.str();
00257 }
00258
00259 std::string writeReturningLocation()
00260 {
00261 llinfos << "apple" << llendl; int this_line = __LINE__;
00262 return locationString(this_line);
00263 }
00264
00265 std::string writeReturningLocationAndFunction()
00266 {
00267 llinfos << "apple" << llendl; int this_line = __LINE__;
00268 return locationString(this_line) + __FUNCTION__;
00269 }
00270
00271 std::string errorReturningLocation()
00272 {
00273 llerrs << "die" << llendl; int this_line = __LINE__;
00274 return locationString(this_line);
00275 }
00276 }
00277
00278 namespace tut
00279 {
00280 template<> template<>
00281 void ErrorTestObject::test<5>()
00282
00283 {
00284 std::string location = writeReturningLocation();
00285
00286
00287 LLError::setPrintLocation(true);
00288 writeReturningLocation();
00289
00290 LLError::setPrintLocation(false);
00291 writeReturningLocation();
00292
00293 ensure_message_does_not_contain(0, location);
00294 ensure_message_contains(1, location);
00295 ensure_message_does_not_contain(2, location);
00296 }
00297 }
00298
00299
00300
00301
00302
00303
00304
00305
00306 std::string logFromGlobal(bool id)
00307 {
00308 llinfos << (id ? "logFromGlobal: " : "") << "hi" << llendl;
00309 return "logFromGlobal";
00310 }
00311
00312 static std::string logFromStatic(bool id)
00313 {
00314 llinfos << (id ? "logFromStatic: " : "") << "hi" << llendl;
00315 return "logFromStatic";
00316 }
00317
00318 namespace
00319 {
00320 std::string logFromAnon(bool id)
00321 {
00322 llinfos << (id ? "logFromAnon: " : "") << "hi" << llendl;
00323 return "logFromAnon";
00324 }
00325 }
00326
00327 namespace Foo {
00328 std::string logFromNamespace(bool id)
00329 {
00330 llinfos << (id ? "Foo::logFromNamespace: " : "") << "hi" << llendl;
00331
00332
00333
00334 return "logFromNamespace";
00335 }
00336 }
00337
00338 namespace
00339 {
00340 class ClassWithNoLogType {
00341 public:
00342 std::string logFromMember(bool id)
00343 {
00344 llinfos << (id ? "ClassWithNoLogType::logFromMember: " : "") << "hi" << llendl;
00345 return "ClassWithNoLogType::logFromMember";
00346 }
00347 static std::string logFromStatic(bool id)
00348 {
00349 llinfos << (id ? "ClassWithNoLogType::logFromStatic: " : "") << "hi" << llendl;
00350 return "ClassWithNoLogType::logFromStatic";
00351 }
00352 };
00353
00354 class ClassWithLogType {
00355 LOG_CLASS(ClassWithLogType);
00356 public:
00357 std::string logFromMember(bool id)
00358 {
00359 llinfos << (id ? "ClassWithLogType::logFromMember: " : "") << "hi" << llendl;
00360 return "ClassWithLogType::logFromMember";
00361 }
00362 static std::string logFromStatic(bool id)
00363 {
00364 llinfos << (id ? "ClassWithLogType::logFromStatic: " : "") << "hi" << llendl;
00365 return "ClassWithLogType::logFromStatic";
00366 }
00367 };
00368
00369 std::string logFromNamespace(bool id) { return Foo::logFromNamespace(id); }
00370 std::string logFromClassWithNoLogTypeMember(bool id) { ClassWithNoLogType c; return c.logFromMember(id); }
00371 std::string logFromClassWithNoLogTypeStatic(bool id) { return ClassWithNoLogType::logFromStatic(id); }
00372 std::string logFromClassWithLogTypeMember(bool id) { ClassWithLogType c; return c.logFromMember(id); }
00373 std::string logFromClassWithLogTypeStatic(bool id) { return ClassWithLogType::logFromStatic(id); }
00374
00375 void ensure_has_once(const std::string& message,
00376 const std::string& actual, const std::string& expected)
00377 {
00378 std::string::size_type n1 = actual.find(expected);
00379 std::string::size_type n2 = std::string::npos;
00380 if (n1 != std::string::npos)
00381 {
00382 n2 = std::string(actual, n1 + expected.size()).find(expected);
00383 }
00384
00385 if (n1 == std::string::npos || n2 != std::string::npos)
00386 {
00387 std::stringstream ss;
00388 ss << message << ": " << "expected to find one copy of " << expected
00389 << " in actual " << actual;
00390 throw tut::failure(ss.str().c_str());
00391 }
00392 }
00393
00394 typedef std::string (*LogFromFunction)(bool);
00395 void testLogNameOnce(TestRecorder& recorder, LogFromFunction f,
00396 const std::string& class_name = "")
00397 {
00398 recorder.clearMessages();
00399 std::string name = f(false);
00400 f(true);
00401
00402 std::string messageWithoutName = recorder.message(0);
00403 std::string messageWithName = recorder.message(1);
00404
00405 ensure_has_once(name + " logged without name",
00406 messageWithoutName, name);
00407 ensure_has_once(name + " logged with name",
00408 messageWithName, name);
00409
00410 if (!class_name.empty())
00411 {
00412 ensure_has_once(name + "logged without name",
00413 messageWithoutName, class_name);
00414 ensure_has_once(name + "logged with name",
00415 messageWithName, class_name);
00416 }
00417 }
00418 }
00419
00420 namespace tut
00421 {
00422 template<> template<>
00423
00424 void ErrorTestObject::test<6>()
00425 {
00426 testLogNameOnce(mRecorder, logFromGlobal);
00427 testLogNameOnce(mRecorder, logFromStatic);
00428 testLogNameOnce(mRecorder, logFromAnon);
00429 testLogNameOnce(mRecorder, logFromNamespace);
00430
00431
00432
00433 testLogNameOnce(mRecorder, logFromClassWithLogTypeMember, "ClassWithLogType");
00434 testLogNameOnce(mRecorder, logFromClassWithLogTypeStatic, "ClassWithLogType");
00435 }
00436 }
00437
00438 namespace
00439 {
00440 std::string innerLogger()
00441 {
00442 llinfos << "inside" << llendl;
00443 return "moo";
00444 }
00445
00446 std::string outerLogger()
00447 {
00448 llinfos << "outside(" << innerLogger() << ")" << llendl;
00449 return "bar";
00450 }
00451
00452 void uberLogger()
00453 {
00454 llinfos << "uber(" << outerLogger() << "," << innerLogger() << ")" << llendl;
00455 }
00456
00457 class LogWhileLogging
00458 {
00459 public:
00460 void print(std::ostream& out) const
00461 {
00462 llinfos << "logging" << llendl;
00463 out << "baz";
00464 }
00465 };
00466
00467 std::ostream& operator<<(std::ostream& out, const LogWhileLogging& l)
00468 { l.print(out); return out; }
00469
00470 void metaLogger()
00471 {
00472 LogWhileLogging l;
00473 llinfos << "meta(" << l << ")" << llendl;
00474 }
00475
00476 }
00477
00478 namespace tut
00479 {
00480 template<> template<>
00481
00482 void ErrorTestObject::test<7>()
00483 {
00484 outerLogger();
00485 ensure_message_contains(0, "inside");
00486 ensure_message_contains(1, "outside(moo)");
00487 ensure_message_count(2);
00488
00489 uberLogger();
00490 ensure_message_contains(2, "inside");
00491 ensure_message_contains(3, "inside");
00492 ensure_message_contains(4, "outside(moo)");
00493 ensure_message_contains(5, "uber(bar,moo)");
00494 ensure_message_count(6);
00495
00496 metaLogger();
00497 ensure_message_contains(6, "logging");
00498 ensure_message_contains(7, "meta(baz)");
00499 ensure_message_count(8);
00500 }
00501
00502 template<> template<>
00503
00504 void ErrorTestObject::test<8>()
00505 {
00506 LLError::setPrintLocation(false);
00507 std::string location = errorReturningLocation();
00508
00509 ensure_message_contains(0, location + "error");
00510 ensure_message_contains(1, "die");
00511 ensure_message_count(2);
00512
00513 ensure("fatal callback called", fatalWasCalled);
00514 }
00515 }
00516
00517 namespace
00518 {
00519 std::string roswell()
00520 {
00521 return "1947-07-08T03:04:05Z";
00522 }
00523
00524 void ufoSighting()
00525 {
00526 llinfos << "ufo" << llendl;
00527 }
00528 }
00529
00530 namespace tut
00531 {
00532 template<> template<>
00533
00534 void ErrorTestObject::test<9>()
00535 {
00536 LLError::setTimeFunction(roswell);
00537
00538 mRecorder.setWantsTime(false);
00539 ufoSighting();
00540 ensure_message_contains(0, "ufo");
00541 ensure_message_does_not_contain(0, roswell());
00542
00543 mRecorder.setWantsTime(true);
00544 ufoSighting();
00545 ensure_message_contains(1, "ufo");
00546 ensure_message_contains(1, roswell());
00547 }
00548
00549 template<> template<>
00550
00551 void ErrorTestObject::test<10>()
00552 {
00553 LLError::setPrintLocation(true);
00554 LLError::setTimeFunction(roswell);
00555 mRecorder.setWantsTime(true);
00556 std::string locationAndFunction = writeReturningLocationAndFunction();
00557
00558 ensure_equals("order is time type location function message",
00559 mRecorder.message(0),
00560 roswell() + " INFO: " + locationAndFunction + ": apple");
00561 }
00562
00563 template<> template<>
00564
00565 void ErrorTestObject::test<11>()
00566 {
00567 TestRecorder altRecorder;
00568 LLError::addRecorder(&altRecorder);
00569
00570 llinfos << "boo" << llendl;
00571
00572 ensure_message_contains(0, "boo");
00573 ensure_equals("alt recorder count", altRecorder.countMessages(), 1);
00574 ensure_contains("alt recorder message 0", altRecorder.message(0), "boo");
00575
00576 LLError::setTimeFunction(roswell);
00577
00578 TestRecorder anotherRecorder;
00579 anotherRecorder.setWantsTime(true);
00580 LLError::addRecorder(&anotherRecorder);
00581
00582 llinfos << "baz" << llendl;
00583
00584 std::string when = roswell();
00585
00586 ensure_message_does_not_contain(1, when);
00587 ensure_equals("alt recorder count", altRecorder.countMessages(), 2);
00588 ensure_does_not_contain("alt recorder message 1", altRecorder.message(1), when);
00589 ensure_equals("another recorder count", anotherRecorder.countMessages(), 1);
00590 ensure_contains("another recorder message 0", anotherRecorder.message(0), when);
00591 }
00592 }
00593
00594 class TestAlpha
00595 {
00596 LOG_CLASS(TestAlpha);
00597 public:
00598 static void doDebug() { lldebugs << "add dice" << llendl; }
00599 static void doInfo() { llinfos << "any idea" << llendl; }
00600 static void doWarn() { llwarns << "aim west" << llendl; }
00601 static void doError() { llerrs << "ate eels" << llendl; }
00602 static void doAll() { doDebug(); doInfo(); doWarn(); doError(); }
00603 };
00604
00605 class TestBeta
00606 {
00607 LOG_CLASS(TestBeta);
00608 public:
00609 static void doDebug() { lldebugs << "bed down" << llendl; }
00610 static void doInfo() { llinfos << "buy iron" << llendl; }
00611 static void doWarn() { llwarns << "bad word" << llendl; }
00612 static void doError() { llerrs << "big easy" << llendl; }
00613 static void doAll() { doDebug(); doInfo(); doWarn(); doError(); }
00614 };
00615
00616 namespace tut
00617 {
00618 template<> template<>
00619
00620 void ErrorTestObject::test<12>()
00621 {
00622 LLError::setDefaultLevel(LLError::LEVEL_WARN);
00623 LLError::setClassLevel("TestBeta", LLError::LEVEL_INFO);
00624
00625 TestAlpha::doAll();
00626 TestBeta::doAll();
00627
00628 ensure_message_contains(0, "aim west");
00629 ensure_message_contains(1, "error");
00630 ensure_message_contains(2, "ate eels");
00631 ensure_message_contains(3, "buy iron");
00632 ensure_message_contains(4, "bad word");
00633 ensure_message_contains(5, "error");
00634 ensure_message_contains(6, "big easy");
00635 ensure_message_count(7);
00636 }
00637
00638 template<> template<>
00639
00640 void ErrorTestObject::test<13>()
00641 {
00642 LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
00643 LLError::setClassLevel("TestBeta", LLError::LEVEL_WARN);
00644 LLError::setFunctionLevel("TestBeta::doInfo", LLError::LEVEL_DEBUG);
00645 LLError::setFunctionLevel("TestBeta::doError", LLError::LEVEL_NONE);
00646
00647 TestBeta::doAll();
00648 ensure_message_contains(0, "buy iron");
00649 ensure_message_contains(1, "bad word");
00650 ensure_message_count(2);
00651 }
00652
00653 template<> template<>
00654
00655
00656 void ErrorTestObject::test<14>()
00657 {
00658 LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
00659 LLError::setFileLevel(LLError::abbreviateFile(__FILE__),
00660 LLError::LEVEL_WARN);
00661 LLError::setClassLevel("TestAlpha", LLError::LEVEL_INFO);
00662 LLError::setFunctionLevel("TestAlpha::doError",
00663 LLError::LEVEL_NONE);
00664 LLError::setFunctionLevel("TestBeta::doError",
00665 LLError::LEVEL_NONE);
00666
00667 TestAlpha::doAll();
00668 TestBeta::doAll();
00669 ensure_message_contains(0, "any idea");
00670 ensure_message_contains(1, "aim west");
00671 ensure_message_contains(2, "bad word");
00672 ensure_message_count(3);
00673 }
00674
00675 template<> template<>
00676
00677 void ErrorTestObject::test<15>()
00678 {
00679 LLError::setDefaultLevel(LLError::LEVEL_NONE);
00680
00681 TestAlpha::doInfo();
00682 ensure_message_count(0);
00683 ensure_equals("first check", LLError::shouldLogCallCount(), 1);
00684 TestAlpha::doInfo();
00685 ensure_message_count(0);
00686 ensure_equals("second check", LLError::shouldLogCallCount(), 1);
00687
00688 LLError::setClassLevel("TestAlpha", LLError::LEVEL_DEBUG);
00689 TestAlpha::doInfo();
00690 ensure_message_count(1);
00691 ensure_equals("third check", LLError::shouldLogCallCount(), 2);
00692 TestAlpha::doInfo();
00693 ensure_message_count(2);
00694 ensure_equals("fourth check", LLError::shouldLogCallCount(), 2);
00695
00696 LLError::setClassLevel("TestAlpha", LLError::LEVEL_WARN);
00697 TestAlpha::doInfo();
00698 ensure_message_count(2);
00699 ensure_equals("fifth check", LLError::shouldLogCallCount(), 3);
00700 TestAlpha::doInfo();
00701 ensure_message_count(2);
00702 ensure_equals("sixth check", LLError::shouldLogCallCount(), 3);
00703 }
00704
00705 template<> template<>
00706
00707 void ErrorTestObject::test<16>()
00708 {
00709 std::string this_file = LLError::abbreviateFile(__FILE__);
00710 LLSD config;
00711 config["print-location"] = true;
00712 config["default-level"] = "DEBUG";
00713
00714 LLSD set1;
00715 set1["level"] = "WARN";
00716 set1["files"][0] = this_file;
00717
00718 LLSD set2;
00719 set2["level"] = "INFO";
00720 set2["classes"][0] = "TestAlpha";
00721
00722 LLSD set3;
00723 set3["level"] = "NONE";
00724 set3["functions"][0] = "TestAlpha::doError";
00725 set3["functions"][1] = "TestBeta::doError";
00726
00727 config["settings"][0] = set1;
00728 config["settings"][1] = set2;
00729 config["settings"][2] = set3;
00730
00731 LLError::configure(config);
00732
00733 TestAlpha::doAll();
00734 TestBeta::doAll();
00735 ensure_message_contains(0, "any idea");
00736 ensure_message_contains(0, this_file);
00737 ensure_message_contains(1, "aim west");
00738 ensure_message_contains(2, "bad word");
00739 ensure_message_count(3);
00740
00741
00742 LLSD config2;
00743 config2["default-level"] = "WARN";
00744
00745 LLError::configure(config2);
00746
00747 TestAlpha::doAll();
00748 TestBeta::doAll();
00749 ensure_message_contains(3, "aim west");
00750 ensure_message_does_not_contain(3, this_file);
00751 ensure_message_contains(4, "error");
00752 ensure_message_contains(5, "ate eels");
00753 ensure_message_contains(6, "bad word");
00754 ensure_message_contains(7, "error");
00755 ensure_message_contains(8, "big easy");
00756 ensure_message_count(9);
00757 }
00758 }
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773