Sprint 288: LibrarySymbolAdvisor (steps 1968-1972)

Specific API symbol recommendation layer. Five new headers:
- LibrarySymbolRecord: functionName/signature/usagePattern/caveats
- LibrarySymbolCatalog: multi-symbol registry per (library, domain)
- LSPSymbolExtractor: parses LSP JSON into LibrarySymbolRecords
- LibrarySymbolAdvisor: ranked symbols for (library, domain) alphabetically
- Sprint288IntegrationSummary: CUDA+nlohmann catalog; cublasSgemm+json::parse verified

25/25 tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Bill
2026-03-02 10:11:23 -07:00
parent 2a40cb1ba5
commit a410c9c756
11 changed files with 720 additions and 0 deletions

View File

@@ -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)

View File

@@ -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 <nlohmann/json.hpp>
#include <string>
#include <vector>
namespace whetstone {
class LSPSymbolExtractor {
public:
struct ExtractionResult {
std::vector<LibrarySymbolRecord> 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<LibrarySymbolRecord> 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<std::string>());
}
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

View File

@@ -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 <algorithm>
#include <optional>
#include <string>
#include <vector>
namespace whetstone {
class LibrarySymbolAdvisor {
public:
// Returns symbols for (libraryId, operationDomain), sorted by functionName.
// Empty if no symbols registered for this pair.
std::vector<LibrarySymbolRecord> 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<LibrarySymbolRecord> 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

View File

@@ -0,0 +1,68 @@
#pragma once
// Step 1969: LibrarySymbolCatalog
// Registry of LibrarySymbolRecords. Multiple symbols per (libraryId, operationDomain).
// Key: libraryId + "::" + operationDomain
#include "LibrarySymbolRecord.h"
#include <string>
#include <unordered_map>
#include <vector>
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<LibrarySymbolRecord> 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<LibrarySymbolRecord> allForLibrary(const std::string& libraryId) const {
std::vector<LibrarySymbolRecord> result;
for (const auto& [k, v] : catalog_) {
for (const auto& r : v) {
if (r.libraryId == libraryId) result.push_back(r);
}
}
return result;
}
std::vector<LibrarySymbolRecord> allForDomain(const std::string& operationDomain) const {
std::vector<LibrarySymbolRecord> 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<std::string, std::vector<LibrarySymbolRecord>> catalog_;
};
} // namespace whetstone

View File

@@ -0,0 +1,19 @@
#pragma once
// Step 1968: LibrarySymbolRecord
// Specific API symbol recommendation for a (library, operationDomain) pair.
#include <string>
#include <vector>
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<std::string> caveats; // known footguns or preconditions
};
} // namespace whetstone

View File

@@ -0,0 +1,83 @@
#pragma once
// Step 1972: Sprint 288 Integration Summary
// Steps 19681972: 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 <string>
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

View File

@@ -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 <iostream>
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;
}

View File

@@ -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 <iostream>
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;
}

View File

@@ -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 <iostream>
#include <string>
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;
}

View File

@@ -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 <iostream>
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;
}

View File

@@ -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 <iostream>
#include <algorithm>
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;
}