diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 03734b5..b9bb01b 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -11114,3 +11114,18 @@ target_include_directories(step1966_test PRIVATE src) add_executable(step1967_test tests/step1967_test.cpp) target_include_directories(step1967_test PRIVATE src) + +add_executable(step1968_test tests/step1968_test.cpp) +target_include_directories(step1968_test PRIVATE src) + +add_executable(step1969_test tests/step1969_test.cpp) +target_include_directories(step1969_test PRIVATE src) + +add_executable(step1970_test tests/step1970_test.cpp) +target_include_directories(step1970_test PRIVATE src) + +add_executable(step1971_test tests/step1971_test.cpp) +target_include_directories(step1971_test PRIVATE src) + +add_executable(step1972_test tests/step1972_test.cpp) +target_include_directories(step1972_test PRIVATE src) diff --git a/editor/src/LSPSymbolExtractor.h b/editor/src/LSPSymbolExtractor.h new file mode 100644 index 0000000..b65e46a --- /dev/null +++ b/editor/src/LSPSymbolExtractor.h @@ -0,0 +1,52 @@ +#pragma once +// Step 1970: LSPSymbolExtractor +// Parses a simplified LSP-like JSON string into LibrarySymbolRecords. +// Input format: {"symbols": [{"name": "...", "signature": "..."}]} +// Requires nlohmann_json (available via vcpkg). + +#include "LibrarySymbolRecord.h" +#include +#include +#include + +namespace whetstone { + +class LSPSymbolExtractor { +public: + struct ExtractionResult { + std::vector records; + bool success = true; + std::string error; + }; + + // Parse lspJson string and produce records for the given (libraryId, operationDomain). + ExtractionResult extract(const std::string& libraryId, + const std::string& operationDomain, + const std::string& lspJson) const { + try { + auto j = nlohmann::json::parse(lspJson); + if (!j.contains("symbols") || !j["symbols"].is_array()) { + return {{}, false, "missing or non-array 'symbols' key"}; + } + std::vector records; + for (const auto& sym : j["symbols"]) { + LibrarySymbolRecord r; + r.libraryId = libraryId; + r.operationDomain = operationDomain; + r.functionName = sym.value("name", std::string{}); + r.signature = sym.value("signature", std::string{}); + r.usagePattern = sym.value("usage", std::string{}); + if (sym.contains("caveats") && sym["caveats"].is_array()) { + for (const auto& c : sym["caveats"]) + r.caveats.push_back(c.get()); + } + if (!r.functionName.empty()) records.push_back(std::move(r)); + } + return {std::move(records), true, ""}; + } catch (const nlohmann::json::exception& e) { + return {{}, false, std::string("json parse error: ") + e.what()}; + } + } +}; + +} // namespace whetstone diff --git a/editor/src/LibrarySymbolAdvisor.h b/editor/src/LibrarySymbolAdvisor.h new file mode 100644 index 0000000..3b341ea --- /dev/null +++ b/editor/src/LibrarySymbolAdvisor.h @@ -0,0 +1,40 @@ +#pragma once +// Step 1971: LibrarySymbolAdvisor +// Given (libraryId, operationDomain, catalog) -> ranked symbol recommendations. +// Ranking: exact domain match, sorted by functionName alphabetically. + +#include "LibrarySymbolCatalog.h" +#include "LibrarySymbolRecord.h" +#include +#include +#include +#include + +namespace whetstone { + +class LibrarySymbolAdvisor { +public: + // Returns symbols for (libraryId, operationDomain), sorted by functionName. + // Empty if no symbols registered for this pair. + std::vector advise(const std::string& libraryId, + const std::string& operationDomain, + const LibrarySymbolCatalog& catalog) const { + auto symbols = catalog.get(libraryId, operationDomain); + std::sort(symbols.begin(), symbols.end(), + [](const LibrarySymbolRecord& a, const LibrarySymbolRecord& b) { + return a.functionName < b.functionName; + }); + return symbols; + } + + // Convenience: returns the top (first alphabetically) symbol, or nullopt. + std::optional topSymbol(const std::string& libraryId, + const std::string& operationDomain, + const LibrarySymbolCatalog& catalog) const { + auto symbols = advise(libraryId, operationDomain, catalog); + if (symbols.empty()) return std::nullopt; + return symbols[0]; + } +}; + +} // namespace whetstone diff --git a/editor/src/LibrarySymbolCatalog.h b/editor/src/LibrarySymbolCatalog.h new file mode 100644 index 0000000..fe08b86 --- /dev/null +++ b/editor/src/LibrarySymbolCatalog.h @@ -0,0 +1,68 @@ +#pragma once +// Step 1969: LibrarySymbolCatalog +// Registry of LibrarySymbolRecords. Multiple symbols per (libraryId, operationDomain). +// Key: libraryId + "::" + operationDomain + +#include "LibrarySymbolRecord.h" +#include +#include +#include + +namespace whetstone { + +class LibrarySymbolCatalog { +public: + void add(LibrarySymbolRecord record) { + auto key = makeKey(record.libraryId, record.operationDomain); + catalog_[key].push_back(std::move(record)); + } + + // Returns all symbols for (libraryId, operationDomain); empty if none. + std::vector get(const std::string& libraryId, + const std::string& operationDomain) const { + auto it = catalog_.find(makeKey(libraryId, operationDomain)); + if (it == catalog_.end()) return {}; + return it->second; + } + + bool has(const std::string& libraryId, const std::string& operationDomain) const { + auto it = catalog_.find(makeKey(libraryId, operationDomain)); + return it != catalog_.end() && !it->second.empty(); + } + + // Total number of symbol records across all keys + size_t size() const { + size_t total = 0; + for (const auto& [k, v] : catalog_) total += v.size(); + return total; + } + + std::vector allForLibrary(const std::string& libraryId) const { + std::vector result; + for (const auto& [k, v] : catalog_) { + for (const auto& r : v) { + if (r.libraryId == libraryId) result.push_back(r); + } + } + return result; + } + + std::vector allForDomain(const std::string& operationDomain) const { + std::vector result; + for (const auto& [k, v] : catalog_) { + for (const auto& r : v) { + if (r.operationDomain == operationDomain) result.push_back(r); + } + } + return result; + } + +private: + static std::string makeKey(const std::string& lib, const std::string& domain) { + return lib + "::" + domain; + } + + std::unordered_map> catalog_; +}; + +} // namespace whetstone diff --git a/editor/src/LibrarySymbolRecord.h b/editor/src/LibrarySymbolRecord.h new file mode 100644 index 0000000..c116f0e --- /dev/null +++ b/editor/src/LibrarySymbolRecord.h @@ -0,0 +1,19 @@ +#pragma once +// Step 1968: LibrarySymbolRecord +// Specific API symbol recommendation for a (library, operationDomain) pair. + +#include +#include + +namespace whetstone { + +struct LibrarySymbolRecord { + std::string libraryId; // e.g. "cublas" + std::string functionName; // e.g. "cublasSgemm" + std::string signature; // e.g. "cublasStatus_t cublasSgemm(cublasHandle_t, ...)" + std::string operationDomain; // e.g. "compute.matrix" + std::string usagePattern; // example call showing typical invocation + std::vector caveats; // known footguns or preconditions +}; + +} // namespace whetstone diff --git a/editor/src/Sprint288IntegrationSummary.h b/editor/src/Sprint288IntegrationSummary.h new file mode 100644 index 0000000..11e4fe2 --- /dev/null +++ b/editor/src/Sprint288IntegrationSummary.h @@ -0,0 +1,83 @@ +#pragma once +// Step 1972: Sprint 288 Integration Summary +// Steps 1968–1972: LibrarySymbolAdvisor +// +// 1968: LibrarySymbolRecord — functionName/signature/operationDomain/usagePattern/caveats +// 1969: LibrarySymbolCatalog — multi-symbol registry per (library, domain) +// 1970: LSPSymbolExtractor — parses LSP JSON into LibrarySymbolRecords +// 1971: LibrarySymbolAdvisor — returns ranked symbols for (library, domain) +// 1972: Integration — CUDA + nlohmann catalog, verify cublasSgemm + json::parse + +#include "LibrarySymbolAdvisor.h" +#include "LibrarySymbolCatalog.h" +#include + +namespace whetstone { + +struct Sprint288IntegrationSummary { + int stepsCompleted = 5; + bool success = true; + std::string sprintName() const { + return "Sprint 288: LibrarySymbolAdvisor"; + } + + static LibrarySymbolCatalog buildSeedCatalog() { + LibrarySymbolCatalog cat; + + // cublas: compute.matrix + cat.add({"cublas", "cublasSgemm", + "cublasStatus_t cublasSgemm(cublasHandle_t, cublasOperation_t, cublasOperation_t," + " int m, int n, int k, const float* alpha, const float* A, int lda," + " const float* B, int ldb, const float* beta, float* C, int ldc)", + "compute.matrix", + "cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, m, n, k, &alpha, A, lda, B, ldb, &beta, C, ldc)", + {"requires-cublasCreate-before-use", "device-memory-only"}}); + cat.add({"cublas", "cublasDgemm", + "cublasStatus_t cublasDgemm(cublasHandle_t, cublasOperation_t, cublasOperation_t," + " int m, int n, int k, const double* alpha, const double* A, int lda," + " const double* B, int ldb, const double* beta, double* C, int ldc)", + "compute.matrix", + "cublasDgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, m, n, k, &alpha, A, lda, B, ldb, &beta, C, ldc)", + {"double-precision", "requires-cublasCreate-before-use"}}); + + // nlohmann_json: serialization.json + cat.add({"nlohmann_json", "json::dump", + "std::string nlohmann::json::dump(int indent = -1) const", + "serialization.json", + "std::string s = j.dump(2); // pretty-print with indent=2", + {}}); + cat.add({"nlohmann_json", "json::parse", + "static nlohmann::json nlohmann::json::parse(const std::string& s)", + "serialization.json", + "auto j = nlohmann::json::parse(input_string);", + {"throws-parse_error-on-invalid-json"}}); + + return cat; + } + + static bool verifyCUDALookup(const LibrarySymbolCatalog& cat) { + LibrarySymbolAdvisor adv; + auto syms = adv.advise("cublas", "compute.matrix", cat); + if (syms.empty()) return false; + // cublasDgemm < cublasSgemm alphabetically, so [0]=cublasDgemm, [1]=cublasSgemm + bool foundSgemm = false; + for (const auto& s : syms) { + if (s.functionName == "cublasSgemm") { foundSgemm = true; break; } + } + return foundSgemm; + } + + static bool verifyNlohmannLookup(const LibrarySymbolCatalog& cat) { + LibrarySymbolAdvisor adv; + auto syms = adv.advise("nlohmann_json", "serialization.json", cat); + if (syms.empty()) return false; + // json::dump < json::parse alphabetically, so [0]=json::dump, [1]=json::parse + bool foundParse = false; + for (const auto& s : syms) { + if (s.functionName == "json::parse") { foundParse = true; break; } + } + return foundParse; + } +}; + +} // namespace whetstone diff --git a/editor/tests/step1968_test.cpp b/editor/tests/step1968_test.cpp new file mode 100644 index 0000000..71172eb --- /dev/null +++ b/editor/tests/step1968_test.cpp @@ -0,0 +1,80 @@ +// Step 1968: LibrarySymbolRecord +// +// t1: default fields +// t2: populated record fields +// t3: caveats list +// t4: usagePattern field +// t5: multiple records for different domains are independent + +#include "LibrarySymbolRecord.h" +#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; } + +void t1() { + T(default_fields_empty); + ws::LibrarySymbolRecord r; + C(r.libraryId.empty(), "empty libraryId"); + C(r.functionName.empty(), "empty functionName"); + C(r.signature.empty(), "empty signature"); + C(r.operationDomain.empty(), "empty operationDomain"); + C(r.caveats.empty(), "empty caveats"); + P(); +} + +void t2() { + T(populated_record_fields); + ws::LibrarySymbolRecord r; + r.libraryId = "cublas"; + r.functionName = "cublasSgemm"; + r.signature = "cublasStatus_t cublasSgemm(cublasHandle_t, ...)"; + r.operationDomain = "compute.matrix"; + C(r.libraryId == "cublas", "libraryId"); + C(r.functionName == "cublasSgemm", "functionName"); + C(r.operationDomain == "compute.matrix", "operationDomain"); + C(!r.signature.empty(), "signature set"); + P(); +} + +void t3() { + T(caveats_list); + ws::LibrarySymbolRecord r; + r.caveats = {"requires-device-memory", "no-automatic-handle"}; + C(r.caveats.size() == 2, "2 caveats"); + C(r.caveats[0] == "requires-device-memory", "first caveat"); + C(r.caveats[1] == "no-automatic-handle", "second caveat"); + P(); +} + +void t4() { + T(usage_pattern_field); + ws::LibrarySymbolRecord r; + r.libraryId = "nlohmann_json"; + r.functionName = "json::parse"; + r.usagePattern = "auto j = nlohmann::json::parse(input_string);"; + C(!r.usagePattern.empty(), "usagePattern set"); + C(r.usagePattern.find("parse") != std::string::npos, "contains 'parse'"); + P(); +} + +void t5() { + T(records_for_different_domains_independent); + ws::LibrarySymbolRecord r1{"cublas", "cublasSgemm", "sig1", "compute.matrix", "", {}}; + ws::LibrarySymbolRecord r2{"cublas", "cublasDnorm", "sig2", "compute.statistics","", {}}; + C(r1.libraryId == r2.libraryId, "same library"); + C(r1.operationDomain != r2.operationDomain, "different domains"); + C(r1.functionName != r2.functionName, "different functions"); + P(); +} + +int main() { + std::cout << "Step 1968: LibrarySymbolRecord\n"; + t1(); t2(); t3(); t4(); t5(); + std::cout << "\n" << p << "/" << (p + f) << " passed\n"; + return f > 0 ? 1 : 0; +} diff --git a/editor/tests/step1969_test.cpp b/editor/tests/step1969_test.cpp new file mode 100644 index 0000000..5bace63 --- /dev/null +++ b/editor/tests/step1969_test.cpp @@ -0,0 +1,86 @@ +// Step 1969: LibrarySymbolCatalog +// +// t1: add and has +// t2: get returns all records for (library, domain) +// t3: get returns empty for unknown pair +// t4: allForLibrary and allForDomain +// t5: size counts total records across all keys + +#include "LibrarySymbolCatalog.h" +#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; } + +static ws::LibrarySymbolCatalog makeCatalog() { + ws::LibrarySymbolCatalog cat; + cat.add({"cublas", "cublasSgemm", "sig_sgemm", "compute.matrix", "", {}}); + cat.add({"cublas", "cublasDgemm", "sig_dgemm", "compute.matrix", "", {}}); + cat.add({"nlohmann_json", "json::parse", "sig_parse", "serialization.json", "", {}}); + cat.add({"nlohmann_json", "json::dump", "sig_dump", "serialization.json", "", {}}); + cat.add({"thrust", "thrust::sort", "sig_sort", "compute.parallel", "", {}}); + return cat; +} + +void t1() { + T(add_and_has); + ws::LibrarySymbolCatalog cat; + C(!cat.has("cublas", "compute.matrix"), "not present before add"); + cat.add({"cublas", "cublasSgemm", "sig", "compute.matrix", "", {}}); + C(cat.has("cublas", "compute.matrix"), "present after add"); + C(!cat.has("cublas", "serialization.json"), "different domain not present"); + P(); +} + +void t2() { + T(get_returns_all_for_pair); + auto cat = makeCatalog(); + auto cublas_matrix = cat.get("cublas", "compute.matrix"); + C(cublas_matrix.size() == 2, "2 cublas/compute.matrix records"); + auto nlohmann_json = cat.get("nlohmann_json", "serialization.json"); + C(nlohmann_json.size() == 2, "2 nlohmann/serialization.json records"); + P(); +} + +void t3() { + T(get_returns_empty_for_unknown_pair); + auto cat = makeCatalog(); + auto none = cat.get("unknown_lib", "compute.matrix"); + C(none.empty(), "empty for unknown library"); + auto none2 = cat.get("cublas", "serialization.json"); + C(none2.empty(), "empty for domain cublas has no symbols in"); + C(!cat.has("cublas", "serialization.json"), "has() false"); + P(); +} + +void t4() { + T(allForLibrary_and_allForDomain); + auto cat = makeCatalog(); + auto cublas_all = cat.allForLibrary("cublas"); + C(cublas_all.size() == 2, "2 cublas records total"); + auto matrix_all = cat.allForDomain("compute.matrix"); + C(matrix_all.size() == 2, "2 compute.matrix records"); + auto thrust_all = cat.allForLibrary("thrust"); + C(thrust_all.size() == 1, "1 thrust record"); + P(); +} + +void t5() { + T(size_counts_all_records); + auto cat = makeCatalog(); + C(cat.size() == 5, "5 total records, got " + std::to_string(cat.size())); + ws::LibrarySymbolCatalog empty; + C(empty.size() == 0, "empty catalog size 0"); + P(); +} + +int main() { + std::cout << "Step 1969: LibrarySymbolCatalog\n"; + t1(); t2(); t3(); t4(); t5(); + std::cout << "\n" << p << "/" << (p + f) << " passed\n"; + return f > 0 ? 1 : 0; +} diff --git a/editor/tests/step1970_test.cpp b/editor/tests/step1970_test.cpp new file mode 100644 index 0000000..97b63a1 --- /dev/null +++ b/editor/tests/step1970_test.cpp @@ -0,0 +1,97 @@ +// 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; +} diff --git a/editor/tests/step1971_test.cpp b/editor/tests/step1971_test.cpp new file mode 100644 index 0000000..341982e --- /dev/null +++ b/editor/tests/step1971_test.cpp @@ -0,0 +1,98 @@ +// Step 1971: LibrarySymbolAdvisor +// +// t1: advise returns symbols sorted by functionName +// t2: advise returns empty for unknown pair +// t3: topSymbol returns first alphabetically +// t4: multiple symbols correctly ordered +// t5: advisee scoped to exact (library, domain) pair + +#include "LibrarySymbolAdvisor.h" +#include "LibrarySymbolCatalog.h" +#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; } + +static ws::LibrarySymbolCatalog makeCatalog() { + ws::LibrarySymbolCatalog cat; + cat.add({"cublas", "cublasSgemm", "sig_s", "compute.matrix", "", {}}); + cat.add({"cublas", "cublasDgemm", "sig_d", "compute.matrix", "", {}}); + cat.add({"nlohmann_json", "json::parse", "sig_p", "serialization.json", "", {}}); + cat.add({"nlohmann_json", "json::dump", "sig_du","serialization.json", "", {}}); + return cat; +} + +void t1() { + T(advise_sorted_by_functionName); + auto cat = makeCatalog(); + ws::LibrarySymbolAdvisor adv; + auto syms = adv.advise("cublas", "compute.matrix", cat); + C(syms.size() == 2, "2 cublas symbols"); + // cublasDgemm < cublasSgemm alphabetically + C(syms[0].functionName == "cublasDgemm", "Dgemm first (D < S)"); + C(syms[1].functionName == "cublasSgemm", "Sgemm second"); + P(); +} + +void t2() { + T(advise_empty_for_unknown_pair); + auto cat = makeCatalog(); + ws::LibrarySymbolAdvisor adv; + auto syms = adv.advise("unknown_lib", "compute.matrix", cat); + C(syms.empty(), "empty for unknown library"); + auto syms2 = adv.advise("cublas", "serialization.json", cat); + C(syms2.empty(), "empty for domain cublas has no symbols in"); + P(); +} + +void t3() { + T(topSymbol_returns_first_alphabetically); + auto cat = makeCatalog(); + ws::LibrarySymbolAdvisor adv; + auto top = adv.topSymbol("nlohmann_json", "serialization.json", cat); + C(top.has_value(), "has top symbol"); + // json::dump < json::parse alphabetically + C(top->functionName == "json::dump", "json::dump is top (d < p)"); + auto none = adv.topSymbol("missing", "missing", cat); + C(!none.has_value(), "nullopt for missing pair"); + P(); +} + +void t4() { + T(nlohmann_symbols_correct_order); + auto cat = makeCatalog(); + ws::LibrarySymbolAdvisor adv; + auto syms = adv.advise("nlohmann_json", "serialization.json", cat); + C(syms.size() == 2, "2 nlohmann symbols"); + C(syms[0].functionName == "json::dump", "dump first (d < p)"); + C(syms[1].functionName == "json::parse", "parse second"); + // Both symbols in serialization.json (parse IS present) + bool hasParse = false; + for (const auto& s : syms) if (s.functionName == "json::parse") hasParse = true; + C(hasParse, "json::parse present in results"); + P(); +} + +void t5() { + T(advise_scoped_to_exact_pair); + auto cat = makeCatalog(); + ws::LibrarySymbolAdvisor adv; + // cublas symbols only in compute.matrix, not compute.parallel + auto par = adv.advise("cublas", "compute.parallel", cat); + C(par.empty(), "no cublas/compute.parallel symbols"); + // nlohmann symbols only in serialization.json, not serialization.binary + auto bin = adv.advise("nlohmann_json", "serialization.binary", cat); + C(bin.empty(), "no nlohmann/serialization.binary symbols"); + P(); +} + +int main() { + std::cout << "Step 1971: LibrarySymbolAdvisor\n"; + t1(); t2(); t3(); t4(); t5(); + std::cout << "\n" << p << "/" << (p + f) << " passed\n"; + return f > 0 ? 1 : 0; +} diff --git a/editor/tests/step1972_test.cpp b/editor/tests/step1972_test.cpp new file mode 100644 index 0000000..8f9a939 --- /dev/null +++ b/editor/tests/step1972_test.cpp @@ -0,0 +1,82 @@ +// Step 1972: Sprint 288 Integration +// +// t1: buildSeedCatalog has cublas and nlohmann entries +// t2: cublasSgemm found for (cublas, compute.matrix) +// t3: json::parse found for (nlohmann_json, serialization.json) +// t4: verifyCUDALookup and verifyNlohmannLookup both pass +// t5: Sprint288IntegrationSummary metadata correct + +#include "Sprint288IntegrationSummary.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; } + +void t1() { + T(seed_catalog_has_cublas_and_nlohmann); + auto cat = ws::Sprint288IntegrationSummary::buildSeedCatalog(); + C(cat.has("cublas", "compute.matrix"), "cublas/compute.matrix"); + C(cat.has("nlohmann_json", "serialization.json"), "nlohmann_json/serialization.json"); + C(cat.size() == 4, "4 total records, got " + std::to_string(cat.size())); + P(); +} + +void t2() { + T(cublasSgemm_found_for_cublas_compute_matrix); + auto cat = ws::Sprint288IntegrationSummary::buildSeedCatalog(); + ws::LibrarySymbolAdvisor adv; + auto syms = adv.advise("cublas", "compute.matrix", cat); + C(syms.size() == 2, "2 cublas/compute.matrix symbols"); + bool hasSgemm = false; + for (const auto& s : syms) if (s.functionName == "cublasSgemm") hasSgemm = true; + C(hasSgemm, "cublasSgemm present"); + // Verify signature mentions handle + for (const auto& s : syms) { + if (s.functionName == "cublasSgemm") { + C(s.signature.find("cublasHandle_t") != std::string::npos, "signature has handle"); + C(!s.caveats.empty(), "cublasSgemm has caveats"); + } + } + P(); +} + +void t3() { + T(json_parse_found_for_nlohmann_serialization_json); + auto cat = ws::Sprint288IntegrationSummary::buildSeedCatalog(); + ws::LibrarySymbolAdvisor adv; + auto syms = adv.advise("nlohmann_json", "serialization.json", cat); + C(syms.size() == 2, "2 nlohmann/serialization.json symbols"); + bool hasParse = false; + for (const auto& s : syms) if (s.functionName == "json::parse") hasParse = true; + C(hasParse, "json::parse present"); + P(); +} + +void t4() { + T(verify_CUDA_and_nlohmann_lookups_pass); + auto cat = ws::Sprint288IntegrationSummary::buildSeedCatalog(); + C(ws::Sprint288IntegrationSummary::verifyCUDALookup(cat), "verifyCUDALookup"); + C(ws::Sprint288IntegrationSummary::verifyNlohmannLookup(cat), "verifyNlohmannLookup"); + P(); +} + +void t5() { + T(sprint288_metadata); + ws::Sprint288IntegrationSummary s; + C(s.stepsCompleted == 5, "5 steps"); + C(s.success, "success"); + C(s.sprintName() == "Sprint 288: LibrarySymbolAdvisor", "name"); + P(); +} + +int main() { + std::cout << "Step 1972: Sprint 288 Integration\n"; + t1(); t2(); t3(); t4(); t5(); + std::cout << "\n" << p << "/" << (p + f) << " passed\n"; + return f > 0 ? 1 : 0; +}