#include #include #include #include #include #include "data.h" #include "episodelistview.h" #include "pl.h" #include "util.h" #include "win.h" extern CfgA& g_cfg; struct Test { const char* name = {0}; char error[64] = {0}; Test(const char* name) : name(name) {} }; #define CAT(a, b) a##b #define APPLY(a, ...) a(__VA_ARGS__) #define TESTS struct APPLY(CAT, tests_, __COUNTER__) #define TEST(id) }; struct id : public Test { id() : Test(#id) #define FAIL(...) do { Sprintf(error, __VA_ARGS__); return; } while (0) TESTS { TEST(StrcpyWithSmallerDestination) { char src[15], dst[10]; Sprintf(src, "abcdefghijklmn"); Strcpy(dst, src); if (dst[8] != 'i') FAIL("dst[8] is not 'i', but '%c'", dst[8]); if (dst[9] != 0) FAIL("dst is not NUL-terminated"); } TEST(EpisodeDataFromWeb) { ElvDataA e; DlvDataA d; FromWeb(10, e, d); if (wcscmp(e.title, L"Pro Soccer Player Blackmail Case") != 0) FAIL("title is not correct"); if (wcscmp(d.date, L"March 11, 1996") != 0) FAIL("date is not correct"); } TEST(EpisodeDataFromProlog) { ElvDataA e; DlvDataA d; FromProlog(10, e); FromProlog(10, d); if (wcscmp(e.title, L"Pro Soccer Player Blackmail Case") != 0) FAIL("title is not correct"); if (wcscmp(d.date, L"March 11, 1996") != 0) FAIL("date is not correct"); } TEST(IO) { ElvDataA e1_0, e2_0; FromProlog(6, e1_0); FromProlog(10, e2_0); /* Write two ElvDataA structs to disk. */ { FileView fv{L"tmp.dat", 2}; memcpy(fv+0, &e1_0, sizeof(e1_0)); memcpy(fv+1, &e2_0, sizeof(e2_0)); } /* Read first ElvDataA struct from disk using * ElvDataA-typed FileView. */ { FileView fv{L"tmp.dat", 2}; const ElvDataA& e1 = *fv; if (e1.version != 'a') FAIL("version is different"); if (e1_0.rating != e1.rating) FAIL("rating is different (%d/%d)", e1_0.rating, e1.rating); if (e1_0.bWatched != e1.bWatched) FAIL("bWatched is different"); if (e1_0.bTVOriginal != e1.bTVOriginal) FAIL("bTVOriginal is different"); if (wcscmp(e1_0.sRating, e1.sRating) != 0) FAIL("sRating is different"); if (wcscmp(e1_0.siEp, e1.siEp) != 0) FAIL("siEp is different"); if (wcscmp(e1_0.title, e1.title) != 0) FAIL("title is different"); } /* Read second ElvDataA struct from disk using * unsigned char-typed FileView. */ { FileView fv{L"tmp.dat", 2*sizeof(ElvDataA)}; ElvDataA& e2 = *reinterpret_cast(&fv.At(sizeof(ElvDataA))); if (e2_0.rating != e2.rating) FAIL("rating is different (%d/%d)", e2_0.rating, e2.rating); if (e2_0.bWatched != e2.bWatched) FAIL("bWatched is different"); if (e2_0.bTVOriginal != e2.bTVOriginal) FAIL("bTVOriginal is different"); if (wcscmp(e2_0.sRating, e2.sRating) != 0) FAIL("sRating is different"); if (wcscmp(e2_0.siEp, e2.siEp) != 0) FAIL("siEp is different"); if (wcscmp(e2_0.title, e2.title) != 0) FAIL("title is different"); } //DeleteFile(L"tmp.dat"); } TEST(MigrateElvDataFromPrologToDisk) { int cEp; if (!Pl("episode_data","episode_count",&cEp)) return; { FileView fv{L"tmp.dat", g_cfg.cEp+128u}; ElvDataA* p = fv; for (int iEp = 1; iEp <= cEp; iEp++) { ElvDataA e; FromProlog(iEp, e); memcpy(p, &e, sizeof(e)); p++; } } { FileView fv{L"tmp.dat", g_cfg.cEp+128u}; ElvDataA& e = fv.At(9); if (wcscmp(e.title, L"Pro Soccer Player Blackmail Case") != 0) FAIL("title is not correct"); } //DeleteFile(L"tmp.dat"); } TEST(SampleConfigurationToDisk) { CfgA cfg_0; { FileView fv{L"tmp.dat", 1}; Wcscpy(cfg_0.url, L"https://animixplay.to/v1/detective-conan/ep"); memcpy(fv, &cfg_0, sizeof(cfg_0)); } { FileView fv{L"tmp.dat", 1}; const CfgA& cfg = fv.At(0); if (cfg_0.bViewWatched != cfg.bViewWatched) FAIL("bViewWatched is different"); if (wcscmp(cfg_0.url, cfg.url) != 0) FAIL("url is not correct"); } //DeleteFile(L"tmp.dat"); } // TEST(MigrateCfg) // { // FileView fva{L"cfga.dat", 1}; // FileView fvb = FileView::Initialized(L"cfgb.dat", 1); // fvb->bViewWatched = fva->bViewWatched; // fvb->bViewTVOriginal = fva->bViewTVOriginal; // fvb->iSortCol = fva->iSortCol; // fvb->iFocus = fva->iFocus; // fvb->heightDlv = fva->heightDlv; // Wcscpy(fvb->limitScreenwriter, fva->limitScreenwriter); // Wcscpy(fvb->root, fva->root); // Wcscpy(fvb->glob, fva->glob); // Wcscpy(fvb->url, fva->url); // } TEST(MigrateDlvDataFromPrologToDisk) { int cEp; if (!Pl("episode_data","episode_count",&cEp)) return; { FileView fv{L"tmp.dat", g_cfg.cEp+128u}; DlvDataA* p = fv; for (int iEp = 1; iEp <= cEp; iEp++) { DlvDataA d; FromProlog(iEp, d); memcpy(p, &d, sizeof(d)); p++; } } { FileView fv{L"tmp.dat", g_cfg.cEp+128u}; DlvDataA& e = fv.At(9); if (wcscmp(e.date, L"March 11, 1996") != 0) FAIL("date is not correct"); } //DeleteFile(L"tmp.dat"); } TEST(DownloadData) { WcharPtr title, wiki, date, source, hint; int i = 1053; /* This is slow. */ while(Pl("episode_data","fetch_episode_data",++i,&title,&wiki,&date,&source,&hint)) { extern FileView g_fvElv; extern FileView g_fvDlv; ElvDataA& e = g_fvElv.At(i-1); Wcscpy(e.title, title); if (!e.siEp[0]) Swprintf(e.siEp, L"%d", i); DlvDataA& d = g_fvDlv.At(i-1); Wcscpy(d.wiki, wiki); Wcscpy(d.date, date); Wcscpy(d.source, source); Wcscpy(d.hint, hint); } extern CfgA& g_cfg; g_cfg.cEp = i; } TEST(Internet) { HINTERNET hi, hiUrl; static unsigned char buf[8'388'608] = {0}; DWORD cbRead; /* Download HTML. */ hi = InternetOpen(L"Episode Browser", INTERNET_OPEN_TYPE_DIRECT, nullptr, nullptr, /*INTERNET_FLAG_ASYNC*/0); if (!hi) goto a; hiUrl = InternetOpenUrl(hi, L"https://www.detectiveconanworld.com/wiki/Anime", nullptr, 0, INTERNET_FLAG_NO_UI, 0); if (!hiUrl) goto b; cbRead = 1; while (cbRead) if (!InternetReadFile(hiUrl, &buf, sizeof(buf), &cbRead)) goto c; //printf("%s\n", buf); InternetCloseHandle(hiUrl); InternetCloseHandle(hi); /* Parse HTML. */ LIBXML_TEST_VERSION; htmlDocPtr doc; doc = htmlReadMemory(reinterpret_cast(buf), sizeof(buf), "https://www.detectiveconanworld.com/wiki/Anime", nullptr, HTML_PARSE_RECOVER|HTML_PARSE_NOERROR|HTML_PARSE_NOWARNING); if (!doc) goto z; xmlXPathContextPtr xpathCtx; xmlXPathObjectPtr xpathObj; xpathCtx = xmlXPathNewContext(doc); if (!xpathCtx) goto y; xpathObj = xmlXPathEvalExpression( reinterpret_cast("//tr"), xpathCtx); if (!xpathObj) goto x; xmlNodeSetPtr nodes; int cNodes; nodes = xpathObj->nodesetval; cNodes = nodes? nodes->nodeNr: 0; printf("%d nodes\n", cNodes); for (int i = 0; i < cNodes; i++) { xmlNodePtr node = nodes->nodeTab[i]; printf("node \"%s\": type %d\n", node->name, node->type); } xmlXPathFreeObject(xpathObj); xmlXPathFreeContext(xpathCtx); xmlFreeDoc(doc); return; x: xmlXPathFreeContext(xpathCtx); y: xmlFreeDoc(doc); z: FAIL(xmlGetLastError()->message); c: InternetCloseHandle(hiUrl); b: InternetCloseHandle(hi); a: FAIL(Win32Error{}.what()); } }; int RunTests() { const Test tests[] = { StrcpyWithSmallerDestination{}, //EpisodeDataFromWeb{}, EpisodeDataFromProlog{}, IO{}, //MigrateElvDataFromPrologToDisk{}, SampleConfigurationToDisk{}, //MigrateCfg{} //MigrateDlvDataFromPrologToDisk{}, //DownloadData{}, Internet{}, }; printf("Results (%llu tests):\n", sizeof(tests)/sizeof(*tests)); int cFailed = 0; for (const Test& t : tests) { const char* msg; if (t.error[0]) { msg = t.error; cFailed++; } else msg = const_cast("SUCCESS"); printf("%s: %s\n", t.name, msg); } printf("%d tests failed.\n", cFailed); return cFailed; }