yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
test_suite_writer.cc
Go to the documentation of this file.
2
3#include <filesystem>
4#include <fstream>
5#include <system_error>
6
7#include "absl/status/status.h"
8#include "absl/strings/str_cat.h"
9#include "absl/strings/str_join.h"
10#include "absl/strings/str_replace.h"
11#include "absl/strings/string_view.h"
12
13namespace yaze {
14namespace cli {
15namespace {
16
17std::string Indent(int count) { return std::string(count, ' '); }
18
19std::string QuoteYaml(absl::string_view value) {
20 std::string escaped(value);
21 absl::StrReplaceAll({{"\\", "\\\\"}, {"\"", "\\\""}}, &escaped);
22 return absl::StrCat("\"", escaped, "\"");
23}
24
25void AppendLine(std::string* out, int indent, absl::string_view line) {
26 out->append(Indent(indent));
27 out->append(line.data(), line.size());
28 out->append("\n");
29}
30
31void AppendScalar(std::string* out, int indent, absl::string_view key,
32 absl::string_view value, bool quote) {
33 out->append(Indent(indent));
34 out->append(key.data(), key.size());
35 out->append(":");
36 if (!value.empty()) {
37 out->append(" ");
38 if (quote) {
39 out->append(QuoteYaml(value));
40 } else {
41 out->append(value.data(), value.size());
42 }
43 }
44 out->append("\n");
45}
46
47std::string FormatDuration(int seconds) {
48 if (seconds <= 0) {
49 return "0s";
50 }
51 if (seconds % 60 == 0) {
52 return absl::StrCat(seconds / 60, "m");
53 }
54 return absl::StrCat(seconds, "s");
55}
56
57std::string FormatBool(bool value) { return value ? "true" : "false"; }
58
59std::string JoinQuotedList(const std::vector<std::string>& values) {
60 if (values.empty()) {
61 return "[]";
62 }
63 std::vector<std::string> quoted;
64 quoted.reserve(values.size());
65 for (const auto& v : values) {
66 quoted.push_back(QuoteYaml(v));
67 }
68 return absl::StrCat("[", absl::StrJoin(quoted, ", "), "]");
69}
70
71} // namespace
72
73std::string BuildTestSuiteYaml(const TestSuiteDefinition& suite) {
74 std::string output;
75
76 if (!suite.name.empty()) {
77 AppendScalar(&output, 0, "name", suite.name, /*quote=*/true);
78 } else {
79 AppendScalar(&output, 0, "name", "Unnamed Suite", /*quote=*/true);
80 }
81 if (!suite.description.empty()) {
82 AppendScalar(&output, 0, "description", suite.description,
83 /*quote=*/true);
84 }
85 if (!suite.version.empty()) {
86 AppendScalar(&output, 0, "version", suite.version, /*quote=*/true);
87 }
88
89 AppendLine(&output, 0, "config:");
90 AppendScalar(&output, 2, "timeout_per_test",
91 FormatDuration(suite.config.timeout_seconds),
92 /*quote=*/false);
93 AppendScalar(&output, 2, "retry_on_failure",
94 absl::StrCat(suite.config.retry_on_failure),
95 /*quote=*/false);
96 AppendScalar(&output, 2, "parallel_execution",
97 FormatBool(suite.config.parallel_execution),
98 /*quote=*/false);
99
100 AppendLine(&output, 0, "test_groups:");
101 for (size_t i = 0; i < suite.groups.size(); ++i) {
102 const TestGroupDefinition& group = suite.groups[i];
103 AppendLine(&output, 2, "- name: " + QuoteYaml(group.name));
104 if (!group.description.empty()) {
105 AppendScalar(&output, 4, "description", group.description,
106 /*quote=*/true);
107 }
108 if (!group.depends_on.empty()) {
109 AppendScalar(&output, 4, "depends_on",
110 JoinQuotedList(group.depends_on), /*quote=*/false);
111 }
112
113 AppendLine(&output, 4, "tests:");
114 for (const TestCaseDefinition& test : group.tests) {
115 AppendLine(&output, 6, "- path: " + QuoteYaml(test.script_path));
116 if (!test.name.empty() && test.name != test.script_path) {
117 AppendScalar(&output, 8, "name", test.name, /*quote=*/true);
118 }
119 if (!test.description.empty()) {
120 AppendScalar(&output, 8, "description", test.description,
121 /*quote=*/true);
122 }
123 if (!test.tags.empty()) {
124 AppendScalar(&output, 8, "tags", JoinQuotedList(test.tags),
125 /*quote=*/false);
126 }
127 if (!test.parameters.empty()) {
128 AppendLine(&output, 8, "parameters:");
129 for (const auto& [key, value] : test.parameters) {
130 AppendScalar(&output, 10, key, value, /*quote=*/true);
131 }
132 }
133 }
134
135 if (!group.tests.empty() && i + 1 < suite.groups.size()) {
136 output.append("\n");
137 }
138 }
139
140 return output;
141}
142
144 const std::string& path, bool overwrite) {
145 std::filesystem::path output_path(path);
146 std::error_code ec;
147 if (!overwrite && std::filesystem::exists(output_path, ec)) {
148 if (!ec) {
149 return absl::AlreadyExistsError(
150 absl::StrCat("Test suite file already exists: ", path));
151 }
152 }
153
154 std::filesystem::path parent = output_path.parent_path();
155 if (!parent.empty()) {
156 std::filesystem::create_directories(parent, ec);
157 if (ec) {
158 return absl::InternalError(absl::StrCat(
159 "Failed to create directories for ", path, ": ", ec.message()));
160 }
161 }
162
163 std::ofstream stream(output_path, std::ios::out | std::ios::trunc);
164 if (!stream.is_open()) {
165 return absl::InternalError(
166 absl::StrCat("Failed to open file for writing: ", path));
167 }
168
169 std::string yaml = BuildTestSuiteYaml(suite);
170 stream << yaml;
171 stream.close();
172
173 if (!stream) {
174 return absl::InternalError(
175 absl::StrCat("Failed to write test suite to ", path));
176 }
177 return absl::OkStatus();
178}
179
180} // namespace cli
181} // namespace yaze
void AppendLine(std::string *out, int indent, absl::string_view line)
std::string JoinQuotedList(const std::vector< std::string > &values)
void AppendScalar(std::string *out, int indent, absl::string_view key, absl::string_view value, bool quote)
std::string BuildTestSuiteYaml(const TestSuiteDefinition &suite)
absl::Status WriteTestSuiteToFile(const TestSuiteDefinition &suite, const std::string &path, bool overwrite)
Main namespace for the application.
std::vector< TestCaseDefinition > tests
Definition test_suite.h:35
std::vector< std::string > depends_on
Definition test_suite.h:34
std::vector< TestGroupDefinition > groups
Definition test_suite.h:43