Source
tests/purrc/compile_run_test.cpp
1
// Copyright (c) 2026 BigBrain LLC. MIT-licensed (see LICENSE).2
// Original work; see ACKNOWLEDGMENTS.md for the open-source ideas we build upon.3
// End-to-end pipeline test: compile a .purr with purrc into a loadable module,4
// then run it with the cheatah runtime and check what it prints.6
#include <array>7
#include <cstdio>8
#include <cstdlib>9
#include <fstream>10
#include <string>12
#include <sys/wait.h>14
#include <gtest/gtest.h>16
#ifndef PURRC_PATH17
#define PURRC_PATH ""18
#endif19
#ifndef CHEATAH_RUNTIME_PATH20
#define CHEATAH_RUNTIME_PATH ""21
#endif22
#ifndef PURR_TEST_TMP23
#define PURR_TEST_TMP "."24
#endif26
namespace {28
std::string run_capture(const std::string& cmd, int& exit_code) {29
std::string out;30
FILE* pipe = popen(cmd.c_str(), "r");31
if (pipe == nullptr) {32
exit_code = -1;33
return out;34
}35
std::array<char, 256> buf{};36
while (std::fgets(buf.data(), static_cast<int>(buf.size()), pipe) != nullptr) {37
out += buf.data();38
}39
const int status = pclose(pipe);40
exit_code = WIFEXITED(status) ? WEXITSTATUS(status) : -1;41
return out;42
}44
} // namespace46
TEST(PurrcPipeline, CompilesAndRunsHelloMeow) {47
const std::string tmp = PURR_TEST_TMP;48
const std::string purr = tmp + "/hellomeow_it.purr";49
const std::string mod = tmp + "/hellomeow_it.so";51
{52
std::ofstream f(purr);53
f << "import io\n";54
f << "io.print(\"meow\")\n";55
}57
// 1) Compile with purrc.58
const std::string compile =59
std::string(PURRC_PATH) + " \"" + purr + "\" -o \"" + mod + "\"";60
ASSERT_EQ(std::system(compile.c_str()), 0) << "purrc failed to compile the program";62
// 2) Run it with the cheatah runtime and capture stdout.63
int rc = -1;64
const std::string out = run_capture(std::string(CHEATAH_RUNTIME_PATH) + " \"" + mod + "\"", rc);65
EXPECT_EQ(rc, 0);66
EXPECT_EQ(out, "meow\n");67
}69
TEST(PurrcPipeline, CompilesAndRunsStructsFunctionsAndLoops) {70
const std::string tmp = PURR_TEST_TMP;71
const std::string purr = tmp + "/lang_it.purr";72
const std::string mod = tmp + "/lang_it.so";74
{75
std::ofstream f(purr);76
f << "import io\n";77
f << "struct P { x: int\n y: int }\n";78
f << "fn add(a, b) { return a + b }\n";79
f << "let total = 0\n";80
f << "for i in range(1, 5) { total = total + i }\n"; // 1+2+3+4 = 1081
f << "let p = P(2, 3)\n";82
f << "io.print(total, add(p.x, p.y))\n"; // "10 5"83
}85
const std::string compile =86
std::string(PURRC_PATH) + " \"" + purr + "\" -o \"" + mod + "\"";87
ASSERT_EQ(std::system(compile.c_str()), 0) << "purrc failed to compile the program";89
int rc = -1;90
const std::string out = run_capture(std::string(CHEATAH_RUNTIME_PATH) + " \"" + mod + "\"", rc);91
EXPECT_EQ(rc, 0);92
EXPECT_EQ(out, "10 5\n");93
}95
TEST(PurrcPipeline, CompilesAndRunsContainersAndStringConcat) {96
const std::string tmp = PURR_TEST_TMP;97
const std::string purr = tmp + "/cont_it.purr";98
const std::string mod = tmp + "/cont_it.so";100
{101
std::ofstream f(purr);102
f << "import io\n";103
f << "let xs = [2, 3, 5]\n";104
f << "let sum = 0\n";105
f << "for x in xs { sum = sum + x }\n"; // 2+3+5 = 10106
f << "let m = {\"k\": 7}\n";107
f << "let name = \"hello\" + \"-world\"\n"; // string concat108
f << "io.print(len(xs), sum, xs[0], m[\"k\"], name)\n";109
}111
const std::string compile =112
std::string(PURRC_PATH) + " \"" + purr + "\" -o \"" + mod + "\"";113
ASSERT_EQ(std::system(compile.c_str()), 0) << "purrc failed to compile the program";115
int rc = -1;116
const std::string out = run_capture(std::string(CHEATAH_RUNTIME_PATH) + " \"" + mod + "\"", rc);117
EXPECT_EQ(rc, 0);118
EXPECT_EQ(out, "3 10 2 7 hello-world\n");119
}121
TEST(PurrcPipeline, CompilesAndRunsTryExceptRaise) {122
const std::string tmp = PURR_TEST_TMP;123
const std::string purr = tmp + "/exc_it.purr";124
const std::string mod = tmp + "/exc_it.so";126
{127
std::ofstream f(purr);128
f << "import io\n";129
f << "try {\n";130
f << " raise \"boom\"\n";131
f << " io.print(\"unreachable\")\n";132
f << "} except e {\n";133
f << " io.print(\"caught:\", e)\n";134
f << "}\n";135
f << "io.print(\"after\")\n";136
}138
const std::string compile =139
std::string(PURRC_PATH) + " \"" + purr + "\" -o \"" + mod + "\"";140
ASSERT_EQ(std::system(compile.c_str()), 0) << "purrc failed to compile the program";142
int rc = -1;143
const std::string out = run_capture(std::string(CHEATAH_RUNTIME_PATH) + " \"" + mod + "\"", rc);144
EXPECT_EQ(rc, 0);145
EXPECT_EQ(out, "caught: boom\nafter\n");146
}148
TEST(PurrcPipeline, CompilesAndRunsSemicolons) {149
const std::string tmp = PURR_TEST_TMP;150
const std::string purr = tmp + "/semi_it.purr";151
const std::string mod = tmp + "/semi_it.so";153
{154
std::ofstream f(purr);155
f << "import io\n";156
f << "let a = 1; let b = 2;\n"; // separator + terminator on one line157
f << "fn add(x, y) { return x + y; }\n"; // ; inside a function body158
f << "struct P { x: int; y: int }\n"; // ; as struct field separator159
f << "let p = P(3, 4)\n";160
f << "io.print(a, b, add(p.x, p.y));\n"; // 1 2 7161
}163
const std::string compile =164
std::string(PURRC_PATH) + " \"" + purr + "\" -o \"" + mod + "\"";165
ASSERT_EQ(std::system(compile.c_str()), 0) << "purrc failed to compile the program";167
int rc = -1;168
const std::string out = run_capture(std::string(CHEATAH_RUNTIME_PATH) + " \"" + mod + "\"", rc);169
EXPECT_EQ(rc, 0);170
EXPECT_EQ(out, "1 2 7\n");171
}173
TEST(PurrcPipeline, CompilesAndRunsCppEscapeHatch) {174
const std::string tmp = PURR_TEST_TMP;175
const std::string purr = tmp + "/cpp_it.purr";176
const std::string mod = tmp + "/cpp_it.so";178
{179
std::ofstream f(purr);180
f << "import io\n";181
f << "cpp {\n"; // top-level -> file scope182
f << "static long long triple(long long n) { return n * 3; }\n";183
f << "}\n";184
f << "fn demo() {\n";185
f << " let acc = 0\n";186
f << " cpp {\n"; // nested -> inline187
f << " for (int i = 1; i <= 4; ++i) { acc += i; }\n"; // 1+2+3+4 = 10188
f << " }\n";189
f << " return acc + triple(2)\n"; // 10 + 6 = 16190
f << "}\n";191
f << "io.print(\"cpp escape hatch:\", demo())\n";192
}194
const std::string compile =195
std::string(PURRC_PATH) + " \"" + purr + "\" -o \"" + mod + "\"";196
ASSERT_EQ(std::system(compile.c_str()), 0) << "purrc failed to compile the program";198
int rc = -1;199
const std::string out = run_capture(std::string(CHEATAH_RUNTIME_PATH) + " \"" + mod + "\"", rc);200
EXPECT_EQ(rc, 0);201
EXPECT_EQ(out, "cpp escape hatch: 16\n");202
}