static void test_DFGetImageDimensions_gif(void) { // Bullet.gif 21x15 static unsigned char data_bullet[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x15, 0x00, 0x0F, 0x00, 0xB3, 0x00, 0x00, 0xFB, 0xCF, 0x57, 0xFA, 0xD3, 0x5F, 0xFA, 0xD6, 0x66, 0xFC, 0xBE, 0x38, 0xFD, 0xBA, 0x30, 0xFC, 0xC2, 0x3F, 0xFB, 0xCB, 0x50, 0xFD, 0xB7, 0x29, 0xF9, 0xD9, 0x6C, 0xFE, 0xB1, 0x1E, 0xFE, 0xB4, 0x23, 0xFB, 0xC7, 0x48, 0xFE, 0xAF, 0x1A, 0xF9, 0xDC, 0x71, 0xF3, 0x9A, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x01, 0x00, 0x00, 0x0E, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x0F, 0x00, 0x00, 0x04, 0x30, 0xD0, 0xC9, 0x49, 0xAB, 0xBD, 0x38, 0xEB, 0xCD, 0xBB, 0xFF, 0x20, 0x00, 0x52, 0x40, 0x30, 0x4A, 0xA5, 0x90, 0x31, 0x89, 0x72, 0x10, 0x43, 0xB1, 0x18, 0x29, 0x82, 0xB1, 0x2E, 0x2C, 0xD3, 0x81, 0x80, 0x34, 0x17, 0xDC, 0x2B, 0x36, 0xAB, 0x8D, 0x52, 0xA7, 0xD2, 0xC9, 0x21, 0xCA, 0x44, 0x00, 0x00, 0x3B }; static char ext_gif[] = "gif"; int rc; unsigned int width, height; char *errmsg; // Test gif rc = DFGetImageDimensions(data_bullet, sizeof(data_bullet), ext_gif, &width, &height, &errmsg); utassert((rc == 1), "wrong rc"); utassert((width == 21), "wrong width"); utassert((height == 15), "wrong height"); }
static void Test02() { const char* s = "<p>Last paragraph"; HtmlPullParser parser(s, str::Len(s)); HtmlToken* t = parser.Next(); utassert(t && t->IsTag() && t->IsStartTag() && Tag_P == t->tag); t = parser.Next(); utassert(t && t->IsText() && str::EqNIx(t->s, t->sLen, "Last paragraph")); }
static void BencTestParseDict(const char *benc, size_t expectedLen) { BencObj *obj = BencObj::Decode(benc); utassert(obj); utassert(obj->Type() == BT_DICT); utassert(static_cast<BencDict *>(obj)->Length() == expectedLen); BencTestSerialization(obj, benc); delete obj; }
static void test_DFInitOnce(void) { static DFOnce once = DF_ONCE_INIT; DFInitOnce(&once, testOnce); utassert((testOnceCount == 1), "function not called"); DFInitOnce(&once, testOnce); utassert((testOnceCount == 1), "function called twice"); }
static void HtmlParser07() { HtmlParser p; HtmlElement *root = p.Parse("<test umls=ä\xC3\xB6ü Zero=�&#-1;>", CP_UTF8); utassert(1 == p.ElementsCount()); ScopedMem<WCHAR> val(root->GetAttribute("umls")); utassert(str::Eq(val, L"\xE4\xF6\xFC")); val.Set(root->GetAttribute("zerO")); utassert(str::Eq(val, L"\x01??")); }
static void Test05() { const char *simpleCss = "<!-- html { ignore } @ignore this; p { } -->"; CssPullParser parser(simpleCss, str::Len(simpleCss)); const CssSelector *sel; const CssProperty *prop; bool ok = parser.NextRule(); utassert(ok); sel = parser.NextSelector(); utassert(sel && Tag_Html == sel->tag && !sel->clazz && IsSelector(sel, "html")); sel = parser.NextSelector(); utassert(!sel); prop = parser.NextProperty(); utassert(!prop); ok = parser.NextRule(); utassert(ok); sel = parser.NextSelector(); utassert(sel && Tag_P == sel->tag && !sel->clazz && IsSelector(sel, "p")); sel = parser.NextSelector(); utassert(!sel); prop = parser.NextProperty(); utassert(!prop); ok = parser.NextRule(); utassert(!ok); }
static void Test01() { const char *inlineCss = "color: red; text-indent: 20px; /* comment */"; CssPullParser parser(inlineCss, str::Len(inlineCss)); const CssProperty *prop = parser.NextProperty(); utassert(prop && Css_Color == prop->type && IsPropVal(prop, "red")); prop = parser.NextProperty(); utassert(prop && Css_Text_Indent == prop->type && IsPropVal(prop, "20px")); prop = parser.NextProperty(); utassert(!prop); }
static void Test07() { const char *simpleCss = " span\n{ color: red }\n\tp /* plain paragraph */ , p#id { }"; CssPullParser parser(simpleCss, str::Len(simpleCss)); bool ok = parser.NextRule(); utassert(ok); ok = parser.NextRule(); utassert(ok); ok = parser.NextRule(); utassert(!ok); }
static void BencTestRoundtrip(BencObj *obj) { ScopedMem<char> encoded(obj->Encode()); utassert(encoded); size_t len; BencObj *obj2 = BencObj::Decode(encoded, &len); utassert(obj2 && len == str::Len(encoded)); ScopedMem<char> roundtrip(obj2->Encode()); utassert(str::Eq(encoded, roundtrip)); delete obj2; }
static void HtmlParser11() { HtmlParser p; HtmlElement *root = p.Parse("<root/><!-- comment -->"); utassert(1 == p.ElementsCount()); utassert(0 == p.TotalAttrCount()); utassert(root && root->NameIs("root")); root = p.Parse("<root><!---></root>"); utassert(!root); }
static void Test02() { const char *inlineCss = "font-family: 'Courier New', \"Times New Roman\", Arial ; font: 12pt Georgia bold"; CssPullParser parser(inlineCss, str::Len(inlineCss)); const CssProperty *prop = parser.NextProperty(); utassert(prop && Css_Font_Family == prop->type && IsPropVal(prop, "'Courier New', \"Times New Roman\", Arial")); prop = parser.NextProperty(); utassert(prop && Css_Font == prop->type && IsPropVal(prop, "12pt Georgia bold")); prop = parser.NextProperty(); utassert(!prop); }
static void BencTestParseInt() { struct { const char * benc; bool valid; int64_t value; } testData[] = { { NULL, false }, { "", false }, { "a", false }, { "0", false }, { "i", false }, { "ie", false }, { "i0", false }, { "i1", false }, { "i23", false }, { "i-", false }, { "i-e", false }, { "i-0e", false }, { "i23f", false }, { "i2-3e", false }, { "i23-e", false }, { "i041e", false }, { "i9223372036854775808e", false }, { "i-9223372036854775809e", false }, { "i0e", true, 0 }, { "i1e", true, 1 }, { "i9823e", true, 9823 }, { "i-1e", true, -1 }, { "i-53e", true, -53 }, { "i123e", true, 123 }, { "i2147483647e", true, INT_MAX }, { "i2147483648e", true, (int64_t)INT_MAX + 1 }, { "i-2147483648e", true, INT_MIN }, { "i-2147483649e", true, (int64_t)INT_MIN - 1 }, { "i9223372036854775807e", true, _I64_MAX }, { "i-9223372036854775808e", true, _I64_MIN }, }; for (int i = 0; i < dimof(testData); i++) { BencObj *obj = BencObj::Decode(testData[i].benc); if (testData[i].valid) { utassert(obj); utassert(obj->Type() == BT_INT); utassert(static_cast<BencInt *>(obj)->Value() == testData[i].value); BencTestSerialization(obj, testData[i].benc); delete obj; } else { utassert(!obj); } } }
static void Test06() { const char *inlineCss = "block: {{ ignore this }} ; color: red; } color: blue"; CssPullParser parser(inlineCss, str::Len(inlineCss)); const CssProperty *prop = parser.NextProperty(); utassert(prop && Css_Unknown == prop->type && IsPropVal(prop, "{{ ignore this }}")); prop = parser.NextProperty(); utassert(prop && Css_Color == prop->type && IsPropVal(prop, "red")); prop = parser.NextProperty(); utassert(!prop); bool ok = parser.NextRule(); utassert(!ok); }
static void Test08() { const char *simpleCss = "broken { brace: \"doesn't close\"; { ignore { color: red; }"; CssPullParser parser(simpleCss, str::Len(simpleCss)); bool ok = parser.NextRule(); utassert(ok); const CssProperty *prop = parser.NextProperty(); utassert(Css_Unknown == prop->type && IsPropVal(prop, "\"doesn't close\"")); prop = parser.NextProperty(); utassert(!prop); ok = parser.NextRule(); utassert(!ok); }
static void Test03() { const char* s = "a < b > c <> d <"; HtmlPullParser parser(s, str::Len(s)); HtmlToken* t = parser.Next(); utassert(t && t->IsText() && str::EqNIx(t->s, t->sLen, "a ")); t = parser.Next(); utassert(t && t->IsText() && str::EqNIx(t->s, t->sLen, "< b > c ")); t = parser.Next(); utassert(t && t->IsText() && str::EqNIx(t->s, t->sLen, "<> d ")); t = parser.Next(); utassert(t && t->IsError() && HtmlToken::UnclosedTag == t->error); t = parser.Next(); utassert(!t); }
static void HtmlParser01() { HtmlParser p; HtmlElement *root = p.Parse("<A><bAh></a>"); utassert(p.ElementsCount() == 2); utassert(Tag_A == root->tag && !root->name); utassert(NULL == root->up); utassert(NULL == root->next); HtmlElement *el = root->down; utassert(NULL == el->firstAttr); utassert(el->NameIs("bah") && el->NameIs("BAH")); utassert(Tag_NotFound == el->tag && str::Eq("bAh", el->name)); utassert(el->up == root); utassert(NULL == el->down); utassert(NULL == el->next); }
static void test_DFMkdirIfAbsent(void) { char *errmsg; int rc; rmdir("xxyz_testdir"); rc = DFMkdirIfAbsent("xxyz_testdir", &errmsg); utassert((rc == 1), "Cannot create directory"); rc = DFMkdirIfAbsent("xxyz_testdir", &errmsg); utassert((rc == 1), "Cannot use existing directory"); rc = rmdir("xxyz_testdir"); utassert((rc == 0), "did not create directory"); }
void SimpleLogTest() { slog::MultiLogger log; log.LogAndFree(str::Dup(L"Don't leak me!")); slog::MemoryLogger logAll; log.AddLogger(&logAll); { slog::MemoryLogger ml; log.AddLogger(&ml); log.Log(L"Test1"); ml.Log(L"ML"); ml.LogFmt(L"%s : %d", L"filen\xE4me.pdf", 25); log.RemoveLogger(&ml); utassert(str::Eq(ml.GetData(), L"Test1\r\nML\r\nfilen\xE4me.pdf : 25\r\n")); } { HANDLE hRead, hWrite; CreatePipe(&hRead, &hWrite, nullptr, 0); slog::FileLogger fl(hWrite); log.AddLogger(&fl); log.Log(L"Test2"); fl.Log(L"FL"); log.LogFmt(L"%s : %d", L"filen\xE4me.pdf", 25); log.RemoveLogger(&fl); char pipeData[32]; char *expected = "Test2\r\nFL\r\nfilen\xC3\xA4me.pdf : 25\r\n"; DWORD len; BOOL ok = ReadFile(hRead, pipeData, sizeof(pipeData), &len, nullptr); utassert(ok && len == str::Len(expected)); pipeData[len] = '\0'; utassert(str::Eq(pipeData, expected)); CloseHandle(hRead); } utassert(str::Eq(logAll.GetData(), L"Test1\r\nTest2\r\nfilen\xE4me.pdf : 25\r\n")); log.RemoveLogger(&logAll); // don't leak the logger, don't crash on logging nullptr log.AddLogger(new slog::DebugLogger()); log.Log(nullptr); }
static void StrObfuscateHelper(const char *orig) { char *obfuscated = StrObfuscate(orig); char *deobfuscated = StrDeobfuscate(obfuscated); utassert(streq(orig, deobfuscated)); free(obfuscated); free(deobfuscated); }
static void Test01() { utassert(IsInlineTag(Tag_A)); utassert(IsInlineTag(Tag_U)); utassert(IsInlineTag(Tag_Span)); utassert(!IsInlineTag(Tag_P)); utassert(IsTagSelfClosing(Tag_Area)); utassert(IsTagSelfClosing(Tag_Link)); utassert(IsTagSelfClosing(Tag_Param)); utassert(!IsTagSelfClosing(Tag_P)); }
static void BencTestParseDicts() { BencObj *obj; obj = BencObj::Decode("d"); utassert(!obj); obj = BencObj::Decode("d123"); utassert(!obj); obj = BencObj::Decode("di12e"); utassert(!obj); obj = BencObj::Decode("di12e2:ale"); utassert(!obj); BencTestParseDict("de", 0); BencTestParseDict("d2:hai35ee", 1); BencTestParseDict("d4:borg1:a3:rum3:leee", 2); BencTestParseDict("d1:Zi-23e2:able3:keyi35ee", 3); }
void BaseUtilTest() { utassert(RoundToPowerOf2(0) == 1); utassert(RoundToPowerOf2(1) == 1); utassert(RoundToPowerOf2(2) == 2); utassert(RoundToPowerOf2(3) == 4); utassert(RoundToPowerOf2(15) == 16); utassert(RoundToPowerOf2((1 << 13) + 1) == (1 << 14)); utassert(RoundToPowerOf2((size_t)-42) == (size_t)-1); utassert(MurmurHash2(NULL, 0) == 0x342CE6C); utassert(MurmurHash2("test", 4) != MurmurHash2("Test", 4)); GeomTest(); }
static void BencTestParseArrays() { BencObj *obj; obj = BencObj::Decode("l"); utassert(!obj); obj = BencObj::Decode("l123"); utassert(!obj); obj = BencObj::Decode("li12e"); utassert(!obj); obj = BencObj::Decode("l2:ie"); utassert(!obj); BencTestParseArray("le", 0); BencTestParseArray("li35ee", 1); BencTestParseArray("llleee", 1); BencTestParseArray("li35ei-23e2:abe", 3); BencTestParseArray("li42e2:teldeedee", 4); }
static void HtmlEntities() { struct { const char* s; int rune; } entities[] = {{"Ü", 220}, {"ü", 252}, {"×", 215}, {"Æ", 198}, {"‌", 8204}, {":", 58}, {"耏", 32783}, {" ", 32}, {"꼴", 44852}, {"Ä", 196}, {"&a3;", -1}, {"&#xz312;", -1}, {"&aer;", -1}}; for (size_t i = 0; i < dimof(entities); i++) { const char* s = entities[i].s; int got; const char* entEnd = ResolveHtmlEntity(s + 1, str::Len(s) - 1, got); utassert(got == entities[i].rune); utassert((-1 == got) == !entEnd); } const char* unchanged[] = {"foo", "", " as;d "}; for (size_t i = 0; i < dimof(unchanged); i++) { const char* s = unchanged[i]; const char* res = ResolveHtmlEntities(s, s + str::Len(s), nullptr); utassert(res == s); } struct { const char* s; const char* res; } changed[] = { // implementation detail: if there is '&' in the string // we always allocate, even if it isn't a valid entity {"a&12", "a&12"}, {"a&x#30", "a&x#30"}, {" b", " b"}, {" ra", " ra"}, {"<", "<"}, {"a&  to end", "a& to end"}, {"  testä ;ö@Pgo", "\xC2\xA0 test\xC3\xA4 ;\xC3\xB6@Pgo"}, }; for (size_t i = 0; i < dimof(changed); i++) { const char* s = changed[i].s; const char* res = ResolveHtmlEntities(s, s + str::Len(s), nullptr); utassert(str::Eq(res, changed[i].res)); str::Free(res); } }
static void strmisc_ut() { utassert(streq(NULL, NULL)); utassert(!streq(NULL, "")); utassert(!streq("", NULL)); utassert(streq("", "")); utassert(!streq(" ", "")); utassert(!streq("", " ")); utassert(streq("abcz", "abcz")); }
static void HtmlParser03() { HtmlParser p; HtmlElement *root = p.Parse("<el att =v"al/>"); utassert(1 == p.ElementsCount()); utassert(1 == p.TotalAttrCount()); utassert(root->NameIs("el")); utassert(NULL == root->next); utassert(NULL == root->up); utassert(NULL == root->down); ScopedMem<WCHAR> val(root->GetAttribute("att")); utassert(str::Eq(val, L"v\"al")); utassert(!root->firstAttr->next); }
static void BencTestParseString() { struct { const char * benc; WCHAR * value; } testData[] = { { NULL, NULL }, { "", NULL }, { "0", NULL }, { "1234", NULL }, { "a", NULL }, { ":", NULL }, { ":z", NULL }, { "1:ab", NULL }, { "3:ab", NULL }, { "-2:ab", NULL }, { "2e:ab", NULL }, { "0:", L"" }, { "1:a", L"a" }, { "2::a", L":a" }, { "4:spam", L"spam" }, { "4:i23e", L"i23e" }, { "5:\xC3\xA4\xE2\x82\xAC", L"\u00E4\u20AC" }, }; for (int i = 0; i < dimof(testData); i++) { BencObj *obj = BencObj::Decode(testData[i].benc); if (testData[i].value) { utassert(obj); utassert(obj->Type() == BT_STRING); ScopedMem<WCHAR> value(static_cast<BencString *>(obj)->Value()); utassert(str::Eq(value, testData[i].value)); BencTestSerialization(obj, testData[i].benc); delete obj; } else { utassert(!obj); } } }
static void test_DFGetImageDimensions_png(void) { // Carrr.png 17x10 static unsigned char data_carr[] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0A, 0x08, 0x03, 0x00, 0x00, 0x00, 0x65, 0xA2, 0x45, 0x90, 0x00, 0x00, 0x00, 0x2C, 0x74, 0x45, 0x58, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x54, 0x69, 0x6D, 0x65, 0x00, 0x54, 0x75, 0x65, 0x20, 0x32, 0x32, 0x20, 0x41, 0x75, 0x67, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x30, 0x30, 0x3A, 0x34, 0x33, 0x3A, 0x31, 0x30, 0x20, 0x2D, 0x30, 0x35, 0x30, 0x30, 0x60, 0x0C, 0x10, 0x58, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4D, 0x45, 0x07, 0xD6, 0x08, 0x16, 0x05, 0x01, 0x29, 0x10, 0xD3, 0x7D, 0xD6, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x1E, 0xC2, 0x00, 0x00, 0x1E, 0xC2, 0x01, 0x6E, 0xD0, 0x75, 0x3E, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4D, 0x41, 0x00, 0x00, 0xB1, 0x8F, 0x0B, 0xFC, 0x61, 0x05, 0x00, 0x00, 0x00, 0x45, 0x50, 0x4C, 0x54, 0x45, 0xFF, 0xFF, 0xFF, 0xCD, 0xC3, 0xB0, 0xD7, 0xCF, 0xC0, 0x80, 0x66, 0x34, 0x73, 0x57, 0x20, 0xE1, 0xDB, 0xD0, 0x8A, 0x72, 0x44, 0x60, 0x40, 0x00, 0x62, 0x43, 0x04, 0xDC, 0xD5, 0xC8, 0xE9, 0xE4, 0xDC, 0x96, 0x81, 0x58, 0x7B, 0x60, 0x2C, 0xAF, 0x9F, 0x80, 0x6C, 0x4E, 0x14, 0x87, 0x6F, 0x40, 0xF5, 0xF3, 0xF0, 0xAA, 0x99, 0x78, 0x64, 0x45, 0x08, 0xF0, 0xED, 0xE8, 0x9E, 0x8A, 0x64, 0xD0, 0xC6, 0xB4, 0x94, 0x7E, 0x54, 0xD6, 0x77, 0xC5, 0x76, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4E, 0x53, 0x00, 0x40, 0xE6, 0xD8, 0x66, 0x00, 0x00, 0x00, 0x4D, 0x49, 0x44, 0x41, 0x54, 0x78, 0xDA, 0x63, 0x60, 0x40, 0x01, 0xBC, 0xEC, 0xBC, 0x30, 0x26, 0x2B, 0x9A, 0x08, 0x97, 0x18, 0x1F, 0x8A, 0x88, 0xB0, 0x08, 0x07, 0xBB, 0x28, 0x92, 0x88, 0x80, 0xA0, 0x10, 0x3B, 0x3B, 0x0B, 0x2F, 0x04, 0xF0, 0x03, 0x45, 0x58, 0xF9, 0xD8, 0x91, 0x01, 0x3F, 0x13, 0xD0, 0x0C, 0x6E, 0x20, 0x83, 0x07, 0xAA, 0x86, 0x97, 0x09, 0x62, 0x0F, 0x1B, 0x3B, 0x07, 0x27, 0xAA, 0x2B, 0x18, 0x98, 0x98, 0x59, 0x18, 0xD0, 0x01, 0x23, 0x9C, 0x05, 0x00, 0x28, 0x72, 0x03, 0x12, 0x3C, 0xA3, 0x13, 0x22, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 }; static char ext_png[] = "png"; int rc; unsigned int width, height; char *errmsg; // Test gif rc = DFGetImageDimensions(data_carr, sizeof(data_carr), ext_png, &width, &height, &errmsg); utassert((rc == 1), "wrong rc"); utassert((width == 17), "wrong width"); utassert((height == 10), "wrong height"); }
static void BencTestParseRawStrings() { BencArray array; array.AddRaw("a\x82"); array.AddRaw("a\x82", 1); BencString *raw = array.GetString(0); utassert(raw && str::Eq(raw->RawValue(), "a\x82")); BencTestSerialization(raw, "2:a\x82"); raw = array.GetString(1); utassert(raw && str::Eq(raw->RawValue(), "a")); BencTestSerialization(raw, "1:a"); BencDict dict; dict.AddRaw("1", "a\x82"); dict.AddRaw("2", "a\x82", 1); raw = dict.GetString("1"); utassert(raw && str::Eq(raw->RawValue(), "a\x82")); BencTestSerialization(raw, "2:a\x82"); raw = dict.GetString("2"); utassert(raw && str::Eq(raw->RawValue(), "a")); BencTestSerialization(raw, "1:a"); }
void HtmlPrettyPrintTest() { size_t lenOut; AutoFree data; data.Set(PrettyPrintHtml("<p><b>Test</b></p>", (size_t)-1, lenOut)); utassert(str::Len(data) == lenOut && str::Eq(data, "<p><b>Test</b></p>\n")); data.Set(PrettyPrintHtml("<p><b>Test</p>", (size_t)-1, lenOut)); utassert(str::Len(data) == lenOut && str::Eq(data, "<p><b>Test</p>\n")); data.Set(PrettyPrintHtml("<html><body><p>Content</p></body></html>", (size_t)-1, lenOut)); utassert(str::Len(data) == lenOut && str::Eq(data, "<html>\n\t<body>\n\t\t<p>Content</p>\n\t</body>\n</html>\n")); data.Set(PrettyPrintHtml("<html><body><p>Content</html></body>", (size_t)-1, lenOut)); // TODO: add newline before non-matching </html> ? // TODO: insert missing closing tags (</p> and </body>)? utassert(str::Len(data) == lenOut && str::Eq(data, "<html>\n\t<body>\n\t\t<p>Content</html>\n</body>\n")); data.Set(PrettyPrintHtml("<p attr=' value '><b> bold text </b> </p>", (size_t)-1, lenOut)); // TODO: normalize whitespace? utassert(str::Len(data) == lenOut && str::Eq(data, "<p attr=' value '><b> bold text </b></p>\n")); }