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"
18 return std::string(count,
' ');
22 std::string escaped(value);
23 absl::StrReplaceAll({{
"\\",
"\\\\"}, {
"\"",
"\\\""}}, &escaped);
24 return absl::StrCat(
"\"", escaped,
"\"");
27void AppendLine(std::string* out,
int indent, absl::string_view line) {
28 out->append(
Indent(indent));
29 out->append(line.data(), line.size());
33void AppendScalar(std::string* out,
int indent, absl::string_view key,
34 absl::string_view value,
bool quote) {
35 out->append(
Indent(indent));
36 out->append(key.data(), key.size());
43 out->append(value.data(), value.size());
53 if (seconds % 60 == 0) {
54 return absl::StrCat(seconds / 60,
"m");
56 return absl::StrCat(seconds,
"s");
60 return value ?
"true" :
"false";
67 std::vector<std::string> quoted;
68 quoted.reserve(values.size());
69 for (
const auto& v : values) {
72 return absl::StrCat(
"[", absl::StrJoin(quoted,
", "),
"]");
80 if (!suite.
name.empty()) {
81 AppendScalar(&output, 0,
"name", suite.
name,
true);
83 AppendScalar(&output, 0,
"name",
"Unnamed Suite",
true);
86 AppendScalar(&output, 0,
"description", suite.
description,
90 AppendScalar(&output, 0,
"version", suite.
version,
true);
93 AppendLine(&output, 0,
"config:");
94 AppendScalar(&output, 2,
"timeout_per_test",
97 AppendScalar(&output, 2,
"retry_on_failure",
100 AppendScalar(&output, 2,
"parallel_execution",
104 AppendLine(&output, 0,
"test_groups:");
105 for (
size_t i = 0; i < suite.
groups.size(); ++i) {
107 AppendLine(&output, 2,
"- name: " + QuoteYaml(group.
name));
109 AppendScalar(&output, 4,
"description", group.
description,
113 AppendScalar(&output, 4,
"depends_on", JoinQuotedList(group.
depends_on),
117 AppendLine(&output, 4,
"tests:");
119 AppendLine(&output, 6,
"- path: " + QuoteYaml(test.script_path));
120 if (!test.name.empty() && test.name != test.script_path) {
121 AppendScalar(&output, 8,
"name", test.name,
true);
123 if (!test.description.empty()) {
124 AppendScalar(&output, 8,
"description", test.description,
127 if (!test.tags.empty()) {
128 AppendScalar(&output, 8,
"tags", JoinQuotedList(test.tags),
131 if (!test.parameters.empty()) {
132 AppendLine(&output, 8,
"parameters:");
133 for (
const auto& [key, value] : test.parameters) {
134 AppendScalar(&output, 10, key, value,
true);
139 if (!group.
tests.empty() && i + 1 < suite.
groups.size()) {
148 const std::string& path,
bool overwrite) {
149 std::filesystem::path output_path(path);
151 if (!overwrite && std::filesystem::exists(output_path, ec)) {
153 return absl::AlreadyExistsError(
154 absl::StrCat(
"Test suite file already exists: ", path));
158 std::filesystem::path parent = output_path.parent_path();
159 if (!parent.empty()) {
160 std::filesystem::create_directories(parent, ec);
162 return absl::InternalError(absl::StrCat(
163 "Failed to create directories for ", path,
": ", ec.message()));
167 std::ofstream stream(output_path, std::ios::out | std::ios::trunc);
168 if (!stream.is_open()) {
169 return absl::InternalError(
170 absl::StrCat(
"Failed to open file for writing: ", path));
178 return absl::InternalError(
179 absl::StrCat(
"Failed to write test suite to ", path));
181 return absl::OkStatus();
void AppendLine(std::string *out, int indent, absl::string_view line)
std::string JoinQuotedList(const std::vector< std::string > &values)
std::string FormatBool(bool value)
void AppendScalar(std::string *out, int indent, absl::string_view key, absl::string_view value, bool quote)
std::string QuoteYaml(absl::string_view value)
std::string FormatDuration(int seconds)
std::string Indent(int count)
std::string BuildTestSuiteYaml(const TestSuiteDefinition &suite)
absl::Status WriteTestSuiteToFile(const TestSuiteDefinition &suite, const std::string &path, bool overwrite)
std::vector< TestCaseDefinition > tests
std::vector< std::string > depends_on
std::vector< TestGroupDefinition > groups