PipelineStageSpec/Registry, PipelineGraph (Kahn topo sort), PipelineExecutionPlan, PipelineParityValidator — 26/26 tests pass. Integration step 1947: Python→Rust→Go→Haskell→TypeScript pipeline verified. Metrics: 7 whetstone calls, 8566 tokens. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
108 lines
3.5 KiB
C++
108 lines
3.5 KiB
C++
// Step 1945: PipelineExecutionPlan
|
|
//
|
|
// t1: addStage and addEdge; isValid
|
|
// t2: executionOrder respects topological order
|
|
// t3: languageSequence returns language per stage in order
|
|
// t4: boundaryPairs detects language transitions
|
|
// t5: isValid false on cycle; empty plan invalid
|
|
|
|
#include "PipelineExecutionPlan.h"
|
|
#include <algorithm>
|
|
#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::PipelineExecutionPlan makePipeline() {
|
|
ws::PipelineExecutionPlan p;
|
|
p.addStage({"ingest", "Python", "ingest", "csv", "records"});
|
|
p.addStage({"normalize", "Rust", "normalize", "records", "normed"});
|
|
p.addStage({"fanout", "Go", "fanout", "normed", "batches"});
|
|
p.addStage({"transform", "Haskell", "transform", "batches", "results"});
|
|
p.addStage({"serialize", "TypeScript", "serialize", "results", "json"});
|
|
p.addEdge("ingest","normalize");
|
|
p.addEdge("normalize","fanout");
|
|
p.addEdge("fanout","transform");
|
|
p.addEdge("transform","serialize");
|
|
return p;
|
|
}
|
|
|
|
void t1(){
|
|
T(addStage_addEdge_isValid);
|
|
auto plan = makePipeline();
|
|
C(plan.isValid(), "pipeline valid");
|
|
P();
|
|
}
|
|
|
|
void t2(){
|
|
T(executionOrder_topological);
|
|
auto plan = makePipeline();
|
|
auto order = plan.executionOrder();
|
|
C(order.size() == 5, "five stages");
|
|
auto pos = [&](const std::string& id){
|
|
for (std::size_t i=0;i<order.size();++i)
|
|
if (order[i].stageId==id) return (int)i;
|
|
return -1;
|
|
};
|
|
C(pos("ingest") < pos("normalize"), "ingest before normalize");
|
|
C(pos("normalize") < pos("fanout"), "normalize before fanout");
|
|
C(pos("fanout") < pos("transform"), "fanout before transform");
|
|
C(pos("transform") < pos("serialize"), "transform before serialize");
|
|
P();
|
|
}
|
|
|
|
void t3(){
|
|
T(languageSequence_in_order);
|
|
auto plan = makePipeline();
|
|
auto langs = plan.languageSequence();
|
|
C(langs.size() == 5, "five languages");
|
|
C(langs[0] == "Python", "first Python");
|
|
C(langs[4] == "TypeScript", "last TypeScript");
|
|
P();
|
|
}
|
|
|
|
void t4(){
|
|
T(boundaryPairs_language_transitions);
|
|
auto plan = makePipeline();
|
|
auto pairs = plan.boundaryPairs();
|
|
// Every adjacent pair differs: Python→Rust, Rust→Go, Go→Haskell, Haskell→TypeScript
|
|
C(pairs.size() == 4, "four boundary pairs");
|
|
bool hasPyRs = false;
|
|
for (auto& pr : pairs)
|
|
if (pr == "Python->Rust") hasPyRs = true;
|
|
C(hasPyRs, "Python->Rust boundary");
|
|
|
|
// Same-language stages produce no boundary
|
|
ws::PipelineExecutionPlan p2;
|
|
p2.addStage({"a","Go","","",""});
|
|
p2.addStage({"b","Go","","",""});
|
|
p2.addEdge("a","b");
|
|
C(p2.boundaryPairs().empty(), "no boundary for same language");
|
|
P();
|
|
}
|
|
|
|
void t5(){
|
|
T(isValid_cycle_and_empty);
|
|
ws::PipelineExecutionPlan empty;
|
|
C(!empty.isValid(), "empty plan invalid");
|
|
|
|
ws::PipelineExecutionPlan cyclic;
|
|
cyclic.addStage({"a","Python","","",""});
|
|
cyclic.addStage({"b","Rust","","",""});
|
|
cyclic.addEdge("a","b");
|
|
cyclic.addEdge("b","a");
|
|
C(!cyclic.isValid(), "cyclic plan invalid");
|
|
P();
|
|
}
|
|
|
|
int main(){
|
|
std::cout << "Step 1945: PipelineExecutionPlan\n";
|
|
t1(); t2(); t3(); t4(); t5();
|
|
std::cout << "\n" << p << "/" << (p+f) << " passed\n";
|
|
return f > 0 ? 1 : 0;
|
|
}
|