// Step 1970: LSPSymbolExtractor // // t1: parses valid JSON into correct records // t2: functionName and signature populated from JSON // t3: caveats array parsed correctly // t4: returns error for invalid JSON // t5: empty symbols array produces empty records; missing name skipped #include "LSPSymbolExtractor.h" #include #include namespace ws = whetstone; static int p = 0, f = 0; #define T(n) { std::cout << " " << #n << "... "; } #define P() { std::cout << "PASS\n"; ++p; } #define F(m) { std::cout << "FAIL: " << m << "\n"; ++f; } #define C(c, m) if (!(c)) { F(m); return; } // Use named delimiter to avoid R"(...)"-terminator collision with JSON content. static const std::string CUBLAS_JSON = R"JSON({ "symbols": [ {"name": "cublasSgemm", "signature": "cublasStatus_t cublasSgemm(...)", "usage": "cublasSgemm(handle, ...)"}, {"name": "cublasDgemm", "signature": "cublasStatus_t cublasDgemm(...)"} ] })JSON"; void t1() { T(parses_valid_json_into_records); ws::LSPSymbolExtractor ex; auto res = ex.extract("cublas", "compute.matrix", CUBLAS_JSON); C(res.success, "success"); C(res.error.empty(), "no error"); C(res.records.size() == 2, "2 records parsed"); C(res.records[0].libraryId == "cublas", "libraryId set"); C(res.records[0].operationDomain == "compute.matrix", "domain set"); P(); } void t2() { T(functionName_and_signature_populated); ws::LSPSymbolExtractor ex; auto res = ex.extract("cublas", "compute.matrix", CUBLAS_JSON); C(res.records[0].functionName == "cublasSgemm", "first functionName"); C(res.records[0].signature.find("cublasSgemm") != std::string::npos, "first signature"); C(res.records[0].usagePattern.find("handle") != std::string::npos, "usagePattern"); C(res.records[1].functionName == "cublasDgemm", "second functionName"); P(); } void t3() { T(caveats_array_parsed); const std::string json = R"JSON({"symbols": [ {"name": "myFunc", "signature": "void myFunc()", "caveats": ["must-call-init-first", "not-thread-safe"]} ]})JSON"; ws::LSPSymbolExtractor ex; auto res = ex.extract("mylib", "mydom", json); C(res.success, "success"); C(res.records.size() == 1, "1 record"); C(res.records[0].caveats.size() == 2, "2 caveats"); C(res.records[0].caveats[0] == "must-call-init-first", "first caveat"); P(); } void t4() { T(returns_error_for_invalid_json); ws::LSPSymbolExtractor ex; auto res = ex.extract("lib", "dom", "{ not valid json !!!"); C(!res.success, "failure for invalid json"); C(!res.error.empty(), "error message set"); C(res.records.empty(), "no records on error"); // Missing symbols key auto res2 = ex.extract("lib", "dom", R"JSON({"other": []})JSON"); C(!res2.success, "failure for missing symbols key"); P(); } void t5() { T(empty_symbols_and_missing_name_skipped); ws::LSPSymbolExtractor ex; auto res = ex.extract("lib", "dom", R"JSON({"symbols": []})JSON"); C(res.success, "success for empty array"); C(res.records.empty(), "no records for empty array"); // Symbol with empty name skipped auto res2 = ex.extract("lib", "dom", R"JSON({"symbols": [{"name": "", "signature": "sig"}]})JSON"); C(res2.success, "success"); C(res2.records.empty(), "empty-name record skipped"); P(); } int main() { std::cout << "Step 1970: LSPSymbolExtractor\n"; t1(); t2(); t3(); t4(); t5(); std::cout << "\n" << p << "/" << (p + f) << " passed\n"; return f > 0 ? 1 : 0; }