Repository navigation
Expand file tree
/
Copy pathcli_main_test.cpp
More file actions
124 lines (109 loc) · 3.98 KB
/
Copy pathcli_main_test.cpp
File metadata and controls
124 lines (109 loc) · 3.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/** Copyright 2026 KUMAZAKI Hiroki. Licensed under Apache-2.0. */
// End-to-end tests for the `tinylamb` CLI executable (main.cpp): a SQL script
// arrives on standard input, runs as one implicit transaction, and buffered
// rows print only after the durability barrier.
#include <sys/wait.h>
#include <unistd.h>
#include <array>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <string>
#include <string_view>
#include "gtest/gtest.h"
namespace {
class CliMainTest : public ::testing::Test {
public:
void SetUp() override {
dir_ = std::filesystem::temp_directory_path() /
("cli_main_test-" + std::to_string(::getpid())) /
::testing::UnitTest::GetInstance()->current_test_info()->name();
std::filesystem::create_directories(dir_);
}
void TearDown() override { std::filesystem::remove_all(dir_); }
protected:
// Runs the sibling `tinylamb` executable with `sql` on stdin. Returns the
// process exit status and fills stdout/stderr.
int RunCli(std::string_view sql, std::string* out, std::string* err) {
const std::filesystem::path exe = BinaryPath("tinylamb");
if (!std::filesystem::exists(exe)) {
*err = "tinylamb executable not found next to the test binary";
return -1;
}
const std::filesystem::path input = dir_ / "input.sql";
const std::filesystem::path stdout_path = dir_ / "stdout";
const std::filesystem::path stderr_path = dir_ / "stderr";
{
std::ofstream in(input, std::ios::binary);
in << sql;
}
const std::string command = "/bin/sh -c '" + exe.string() + " " +
(dir_ / "db").string() + " < " +
input.string() + " > " + stdout_path.string() +
" 2> " + stderr_path.string() + "'";
// NOLINTNEXTLINE(cert-env33-c)
const int status = std::system(command.c_str());
ReadFile(stdout_path, out);
ReadFile(stderr_path, err);
if (WIFEXITED(status)) {
return WEXITSTATUS(status);
}
return -1;
}
private:
static std::filesystem::path BinaryPath(std::string_view name) {
std::array<char, 4096> self{};
const ssize_t len =
::readlink("/proc/self/exe", self.data(), self.size() - 1);
if (len <= 0) {
return {name};
}
self[static_cast<size_t>(len)] = '\0';
return std::filesystem::path(self.data()).parent_path() / name;
}
static void ReadFile(const std::filesystem::path& path, std::string* out) {
std::ifstream in(path, std::ios::binary);
out->assign(std::istreambuf_iterator<char>(in),
std::istreambuf_iterator<char>());
}
std::filesystem::path dir_;
};
TEST_F(CliMainTest, ScriptExecutesAndPrintsRowsAfterCommit) {
std::string out;
std::string err;
const int code = RunCli(
"CREATE TABLE t (k INT64, v VARCHAR);"
"INSERT INTO t VALUES (1, 'one'), (2, 'two');"
"SELECT k FROM t ORDER BY k;",
&out, &err);
EXPECT_EQ(code, 0) << true << err;
// Row output uses Row's operator<< format; every buffered statement's
// output (including command tags) prints only after the commit barrier.
EXPECT_NE(out.find("[1]"), std::string::npos) << out;
EXPECT_NE(out.find("[2]"), std::string::npos) << out;
EXPECT_NE(out.find("Insert Rows"), std::string::npos) << out;
}
TEST_F(CliMainTest, FailedStatementAbortsTheWholeScript) {
std::string out;
std::string err;
// The last statement fails (unknown column); nothing may be printed even
// though the earlier SELECT produced a row.
const int code = RunCli(
"CREATE TABLE t (k INT64);"
"INSERT INTO t VALUES (1);"
"SELECT k FROM t;"
"SELECT missing FROM t;",
&out, &err);
EXPECT_EQ(code, 1);
EXPECT_TRUE(out.empty()) << out;
EXPECT_FALSE(err.empty());
}
TEST_F(CliMainTest, EmptyStdinFailsWithUsage) {
std::string out;
std::string err;
const int code = RunCli("", &out, &err);
EXPECT_EQ(code, 2);
EXPECT_TRUE(out.empty());
EXPECT_NE(err.find("no SQL"), std::string::npos) << err;
}
} // namespace