Remove FAKE mode, use /usr/bin/echo for tests

This commit is contained in:
2026-07-20 21:51:39 +03:00
parent 99e9f880ee
commit 41accd5290
5 changed files with 13 additions and 224 deletions
-14
View File
@@ -5,14 +5,6 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
option(FAKE "Fake mode for testing without opencode" OFF)
if(FAKE)
message(STATUS "FAKE mode ON — using fake process executor")
else()
message(STATUS "FAKE mode OFF — using real process executor")
endif()
# PIP # PIP
find_package(PIP REQUIRED) find_package(PIP REQUIRED)
@@ -36,9 +28,6 @@ set(SRCS ${ALL_SRCS})
add_executable(pipeline-runner src/main.cpp ${SRCS} ${HDRS}) add_executable(pipeline-runner src/main.cpp ${SRCS} ${HDRS})
target_include_directories(pipeline-runner PRIVATE src) target_include_directories(pipeline-runner PRIVATE src)
target_link_libraries(pipeline-runner PIP PIP::HTTPServer PIP::Crypt PIP::Console) target_link_libraries(pipeline-runner PIP PIP::HTTPServer PIP::Crypt PIP::Console)
if(FAKE)
target_compile_definitions(pipeline-runner PRIVATE FAKE)
endif()
set_target_properties(pipeline-runner PROPERTIES set_target_properties(pipeline-runner PROPERTIES
INSTALL_RPATH "\$ORIGIN;\$ORIGIN/lib" INSTALL_RPATH "\$ORIGIN;\$ORIGIN/lib"
@@ -49,9 +38,6 @@ set_target_properties(pipeline-runner PROPERTIES
add_executable(test-pipeline-runner tests/test_pipeline.cpp tests/test_runner.cpp ${SRCS}) add_executable(test-pipeline-runner tests/test_pipeline.cpp tests/test_runner.cpp ${SRCS})
target_include_directories(test-pipeline-runner PRIVATE src tests) target_include_directories(test-pipeline-runner PRIVATE src tests)
target_link_libraries(test-pipeline-runner PIP PIP::HTTPServer PIP::Crypt PIP::Console GTest::gtest_main) target_link_libraries(test-pipeline-runner PIP PIP::HTTPServer PIP::Crypt PIP::Console GTest::gtest_main)
if(FAKE)
target_compile_definitions(test-pipeline-runner PRIVATE FAKE)
endif()
include(GoogleTest) include(GoogleTest)
gtest_discover_tests(test-pipeline-runner) gtest_discover_tests(test-pipeline-runner)
+2 -24
View File
@@ -5,45 +5,25 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BUILD_DIR="${SCRIPT_DIR}/build" BUILD_DIR="${SCRIPT_DIR}/build"
REBUILD=false REBUILD=false
FAKE=false
for arg in "$@"; do for arg in "$@"; do
case "$arg" in case "$arg" in
--rebuild) REBUILD=true ;; --rebuild) REBUILD=true ;;
--fake) FAKE=true ;;
esac esac
done done
CMAKE_ARGS=""
if [ "${FAKE}" = true ]; then
CMAKE_ARGS="-DFAKE=ON"
fi
if [ "${REBUILD}" = true ]; then if [ "${REBUILD}" = true ]; then
echo "==> Clean build directory" echo "==> Clean build directory"
rm -rf "${BUILD_DIR}" rm -rf "${BUILD_DIR}"
mkdir -p "${BUILD_DIR}" mkdir -p "${BUILD_DIR}"
echo "==> Configure" echo "==> Configure"
cmake -S "${SCRIPT_DIR}" -B "${BUILD_DIR}" ${CMAKE_ARGS} cmake -S "${SCRIPT_DIR}" -B "${BUILD_DIR}"
fi fi
mkdir -p "${BUILD_DIR}" mkdir -p "${BUILD_DIR}"
if [ -f "${BUILD_DIR}/CMakeCache.txt" ]; then
CACHED_FAKE=$(grep -o "FAKE:BOOL=.\+" "${BUILD_DIR}/CMakeCache.txt" 2>/dev/null || echo "")
if [ "${FAKE}" = true ] && [ "${CACHED_FAKE}" != "FAKE:BOOL=ON" ]; then
echo "==> FAKE flag changed, cleaning build"
rm -rf "${BUILD_DIR}"
mkdir -p "${BUILD_DIR}"
elif [ "${FAKE}" = false ] && [ "${CACHED_FAKE}" = "FAKE:BOOL=ON" ]; then
echo "==> FAKE flag changed, cleaning build"
rm -rf "${BUILD_DIR}"
mkdir -p "${BUILD_DIR}"
fi
fi
if [ ! -f "${BUILD_DIR}/CMakeCache.txt" ]; then if [ ! -f "${BUILD_DIR}/CMakeCache.txt" ]; then
echo "==> Configure" echo "==> Configure"
cmake -S "${SCRIPT_DIR}" -B "${BUILD_DIR}" ${CMAKE_ARGS} cmake -S "${SCRIPT_DIR}" -B "${BUILD_DIR}"
fi fi
echo "==> Build" echo "==> Build"
@@ -55,10 +35,8 @@ rm -rf /tmp/pipeline-runner-test/
--gtest_brief=1 \ --gtest_brief=1 \
--gtest_output=xml:"${BUILD_DIR}/test-results.xml" --gtest_output=xml:"${BUILD_DIR}/test-results.xml"
if [ "${FAKE}" = true ]; then
echo "==> Run Python tests" echo "==> Run Python tests"
timeout 60 python3 -m pytest "${SCRIPT_DIR}/tests" --tb=line -q \ timeout 60 python3 -m pytest "${SCRIPT_DIR}/tests" --tb=line -q \
--junitxml="${BUILD_DIR}/python-test-results.xml" --junitxml="${BUILD_DIR}/python-test-results.xml"
fi
echo "==> Done" echo "==> Done"
-31
View File
@@ -167,36 +167,6 @@ bool PipelineRunner::isRunActive(const PIString & run_id) {
void PipelineRunner::executePipeline(const PIString & run_id, const Pipeline & pipeline) { void PipelineRunner::executePipeline(const PIString & run_id, const Pipeline & pipeline) {
int total = pipeline.prompts.size(); int total = pipeline.prompts.size();
#ifdef FAKE
// Fake mode: simulate all steps and update state atomically
for (int i = 0; i < total; ++i) {
const Prompt & prompt = pipeline.prompts[i];
appendLog(run_id, "[Step " + PIString::fromNumber(i + 1) + "/" + PIString::fromNumber(total) + "] Starting: " + prompt.title);
appendLog(run_id, "[Step " + PIString::fromNumber(i + 1) + "/" + PIString::fromNumber(total) + "] Prompt: " + prompt.text);
piMSleep(100);
appendLog(run_id, "[Step " + PIString::fromNumber(i + 1) + "/" + PIString::fromNumber(total) + "] COMPLETED (returncode: 0)");
}
// Update all step results and run status in a single mutex section
{
PIMutexLocker ml(mutex_);
if (runs_.contains(run_id)) {
for (int i = 0; i < total; ++i) {
runs_[run_id].current_step = i;
runs_[run_id].steps[i].status = RunStatus::Completed;
runs_[run_id].steps[i].returncode = 0;
runs_[run_id].steps[i].output = "FAKE: executed step " + PIString::fromNumber(i) + ": " + pipeline.prompts[i].title;
runs_[run_id].steps[i].error = "";
}
runs_[run_id].status = RunStatus::Completed;
}
active_runs_.remove(run_id);
}
saveRunState(run_id);
appendLog(run_id, "Pipeline finished");
#else
for (int i = 0; i < total; ++i) { for (int i = 0; i < total; ++i) {
const Prompt & prompt = pipeline.prompts[i]; const Prompt & prompt = pipeline.prompts[i];
appendLog(run_id, "[Step " + PIString::fromNumber(i + 1) + "/" + PIString::fromNumber(total) + "] Starting: " + prompt.title); appendLog(run_id, "[Step " + PIString::fromNumber(i + 1) + "/" + PIString::fromNumber(total) + "] Starting: " + prompt.title);
@@ -265,7 +235,6 @@ void PipelineRunner::executePipeline(const PIString & run_id, const Pipeline & p
saveRunState(run_id); saveRunState(run_id);
appendLog(run_id, "Pipeline finished"); appendLog(run_id, "Pipeline finished");
#endif
} }
void PipelineRunner::appendLog(const PIString & run_id, const PIString & line) { void PipelineRunner::appendLog(const PIString & run_id, const PIString & line) {
+1
View File
@@ -37,6 +37,7 @@ class PipelineServer:
"port": port, "port": port,
"pipelines_dir": os.path.join(work_dir, "pipelines") + "/", "pipelines_dir": os.path.join(work_dir, "pipelines") + "/",
"logs_dir": os.path.join(work_dir, "logs") + "/", "logs_dir": os.path.join(work_dir, "logs") + "/",
"opencode_path": "/usr/bin/echo",
} }
if web_root: if web_root:
config["web_root"] = web_root config["web_root"] = web_root
+7 -152
View File
@@ -62,7 +62,7 @@ static Pipeline makeEchoPipeline() {
TEST(RunnerTest, StartRunNotFound) { TEST(RunnerTest, StartRunNotFound) {
setupDirs(); setupDirs();
PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300); PipelineRunner runner(testPipelinesDir, testLogsDir, "/usr/bin/echo", 300);
PIString runId = runner.startRun("nonexistent"); PIString runId = runner.startRun("nonexistent");
EXPECT_TRUE(runId.isEmpty()); EXPECT_TRUE(runId.isEmpty());
@@ -72,7 +72,7 @@ TEST(RunnerTest, StartRunNotFound) {
TEST(RunnerTest, StartRunAndGetState) { TEST(RunnerTest, StartRunAndGetState) {
setupDirs(); setupDirs();
PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300); PipelineRunner runner(testPipelinesDir, testLogsDir, "/usr/bin/echo", 300);
// Create a pipeline that runs 'echo hello' (uses shell command, not opencode) // Create a pipeline that runs 'echo hello' (uses shell command, not opencode)
Pipeline pl; Pipeline pl;
@@ -108,7 +108,7 @@ TEST(RunnerTest, StartRunAndGetState) {
TEST(RunnerTest, GetStateNotFound) { TEST(RunnerTest, GetStateNotFound) {
setupDirs(); setupDirs();
PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300); PipelineRunner runner(testPipelinesDir, testLogsDir, "/usr/bin/echo", 300);
RunState state = runner.getRunState("nonexistent"); RunState state = runner.getRunState("nonexistent");
EXPECT_TRUE(state.run_id.isEmpty()); EXPECT_TRUE(state.run_id.isEmpty());
@@ -118,7 +118,7 @@ TEST(RunnerTest, GetStateNotFound) {
TEST(RunnerTest, IsRunActive) { TEST(RunnerTest, IsRunActive) {
setupDirs(); setupDirs();
PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300); PipelineRunner runner(testPipelinesDir, testLogsDir, "/usr/bin/echo", 300);
Pipeline pl; Pipeline pl;
pl.id = "active-test"; pl.id = "active-test";
@@ -154,7 +154,7 @@ TEST(RunnerTest, RunStatusToString) {
TEST(PersistenceTest, SaveLoadRoundTrip) { TEST(PersistenceTest, SaveLoadRoundTrip) {
setupDirs(); setupDirs();
PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300); PipelineRunner runner(testPipelinesDir, testLogsDir, "/usr/bin/echo", 300);
// Test loadRunState on non-existent file // Test loadRunState on non-existent file
RunState empty = runner.loadRunState("nonexistent"); RunState empty = runner.loadRunState("nonexistent");
@@ -243,7 +243,7 @@ TEST(PersistenceTest, RecoverRuns) {
PIFile::writeAll(completedStatePath, j2.toJSON(PIJSON::Tree).toUTF8()); PIFile::writeAll(completedStatePath, j2.toJSON(PIJSON::Tree).toUTF8());
// New runner should recover only the running state // New runner should recover only the running state
PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300); PipelineRunner runner(testPipelinesDir, testLogsDir, "/usr/bin/echo", 300);
runner.recoverRuns(); runner.recoverRuns();
RunState recovered = runner.getRunState(runId); RunState recovered = runner.getRunState(runId);
@@ -260,7 +260,7 @@ TEST(PersistenceTest, RecoverRuns) {
TEST(PersistenceTest, CompletedRunStatePersisted) { TEST(PersistenceTest, CompletedRunStatePersisted) {
setupDirs(); setupDirs();
PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300); PipelineRunner runner(testPipelinesDir, testLogsDir, "/usr/bin/echo", 300);
Pipeline pl; Pipeline pl;
pl.id = "persist-test"; pl.id = "persist-test";
@@ -292,148 +292,3 @@ TEST(PersistenceTest, CompletedRunStatePersisted) {
cleanupDirs(); cleanupDirs();
} }
#ifdef FAKE
static Pipeline makeFakePipeline(const PIString & id, const PIString & name, int numSteps) {
Pipeline pl;
pl.id = id;
pl.name = name;
pl.working_dir = "/tmp";
pl.created_at = nowISO();
pl.updated_at = pl.created_at;
for (int i = 0; i < numSteps; ++i) {
Prompt p;
p.id = "p" + PIString::fromNumber(i);
p.text = "fake text " + PIString::fromNumber(i);
p.title = "Fake Step " + PIString::fromNumber(i);
p.order = i;
pl.prompts << p;
}
return pl;
}
TEST(FakeRunnerTest, FullPipelineCompletes) {
setupDirs();
PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300);
Pipeline pl = makeFakePipeline("fake-full", "Fake Full Pipeline", 3);
savePipeline(testPipelinesDir, pl);
PIString runId = runner.startRun("fake-full");
EXPECT_FALSE(runId.isEmpty());
// Wait for pipeline to finish (3 steps * 100ms = 300ms, allow extra time)
for (int i = 0; i < 100; ++i) {
piMSleep(50);
RunState state = runner.getRunState(runId);
if (state.status == RunStatus::Completed) {
bool allDone = true;
for (int j = 0; j < state.steps.size(); ++j) {
if (state.steps[j].status != RunStatus::Completed) {
allDone = false;
break;
}
}
if (allDone) break;
}
if (state.status == RunStatus::Error) break;
}
RunState state = runner.getRunState(runId);
EXPECT_EQ(state.status, RunStatus::Completed);
EXPECT_EQ(state.steps.size(), 3);
for (int i = 0; i < 3; ++i) {
EXPECT_EQ(state.steps[i].status, RunStatus::Completed);
EXPECT_EQ(state.steps[i].returncode, 0);
}
// Wait for background thread to finish before cleanup
waitForRun(runner, runId, 2000);
cleanupDirs();
}
TEST(FakeRunnerTest, RunStatusTransitions) {
setupDirs();
PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300);
Pipeline pl = makeFakePipeline("fake-transition", "Fake Transition Pipeline", 2);
savePipeline(testPipelinesDir, pl);
PIString runId = runner.startRun("fake-transition");
EXPECT_FALSE(runId.isEmpty());
// Initially running
RunState state = runner.getRunState(runId);
EXPECT_EQ(state.status, RunStatus::Running);
// Wait for completion (2 steps * 100ms = 200ms, allow extra time)
for (int i = 0; i < 100; ++i) {
piMSleep(50);
state = runner.getRunState(runId);
if (state.status == RunStatus::Completed) {
bool allDone = true;
for (int j = 0; j < state.steps.size(); ++j) {
if (state.steps[j].status != RunStatus::Completed) {
allDone = false;
break;
}
}
if (allDone) break;
}
if (state.status == RunStatus::Error) break;
}
state = runner.getRunState(runId);
EXPECT_EQ(state.status, RunStatus::Completed);
EXPECT_FALSE(runner.isRunActive(runId));
// Wait for background thread to finish before cleanup
waitForRun(runner, runId, 2000);
cleanupDirs();
}
TEST(FakeRunnerTest, StepOutputContainsFakeMarker) {
setupDirs();
PipelineRunner runner(testPipelinesDir, testLogsDir, "opencode", 300);
Pipeline pl = makeFakePipeline("fake-output", "Fake Output Pipeline", 2);
savePipeline(testPipelinesDir, pl);
PIString runId = runner.startRun("fake-output");
EXPECT_FALSE(runId.isEmpty());
// Wait for completion (2 steps * 100ms = 200ms, allow extra time)
for (int i = 0; i < 100; ++i) {
piMSleep(50);
RunState state = runner.getRunState(runId);
if (state.status == RunStatus::Completed) {
bool allDone = true;
for (int j = 0; j < state.steps.size(); ++j) {
if (state.steps[j].status != RunStatus::Completed) {
allDone = false;
break;
}
}
if (allDone) break;
}
if (state.status == RunStatus::Error) break;
}
RunState state = runner.getRunState(runId);
EXPECT_EQ(state.status, RunStatus::Completed);
for (int i = 0; i < state.steps.size(); ++i) {
EXPECT_TRUE(state.steps[i].output.contains("FAKE"));
EXPECT_TRUE(state.steps[i].output.contains("Fake Step " + PIString::fromNumber(i)));
EXPECT_TRUE(state.steps[i].error.isEmpty());
}
// Wait for background thread to finish before cleanup
waitForRun(runner, runId, 2000);
cleanupDirs();
}
#endif