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"
17std::string
Indent(
int count) {
return std::string(count,
' '); }
20 std::string escaped(value);
21 absl::StrReplaceAll({{
"\\",
"\\\\"}, {
"\"",
"\\\""}}, &escaped);
22 return absl::StrCat(
"\"", escaped,
"\"");
25void AppendLine(std::string* out,
int indent, absl::string_view line) {
26 out->append(
Indent(indent));
27 out->append(line.data(), line.size());
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());
41 out->append(value.data(), value.size());
51 if (seconds % 60 == 0) {
52 return absl::StrCat(seconds / 60,
"m");
54 return absl::StrCat(seconds,
"s");
57std::string
FormatBool(
bool value) {
return value ?
"true" :
"false"; }
63 std::vector<std::string> quoted;
64 quoted.reserve(values.size());
65 for (
const auto& v : values) {
68 return absl::StrCat(
"[", absl::StrJoin(quoted,
", "),
"]");
76 if (!suite.
name.empty()) {
77 AppendScalar(&output, 0,
"name", suite.
name,
true);
79 AppendScalar(&output, 0,
"name",
"Unnamed Suite",
true);
82 AppendScalar(&output, 0,
"description", suite.
description,
86 AppendScalar(&output, 0,
"version", suite.
version,
true);
89 AppendLine(&output, 0,
"config:");
90 AppendScalar(&output, 2,
"timeout_per_test",
93 AppendScalar(&output, 2,
"retry_on_failure",
96 AppendScalar(&output, 2,
"parallel_execution",
100 AppendLine(&output, 0,
"test_groups:");
101 for (
size_t i = 0; i < suite.
groups.size(); ++i) {
103 AppendLine(&output, 2,
"- name: " + QuoteYaml(group.
name));
105 AppendScalar(&output, 4,
"description", group.
description,
109 AppendScalar(&output, 4,
"depends_on",
113 AppendLine(&output, 4,
"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,
true);
119 if (!test.description.empty()) {
120 AppendScalar(&output, 8,
"description", test.description,
123 if (!test.tags.empty()) {
124 AppendScalar(&output, 8,
"tags", JoinQuotedList(test.tags),
127 if (!test.parameters.empty()) {
128 AppendLine(&output, 8,
"parameters:");
129 for (
const auto& [key, value] : test.parameters) {
130 AppendScalar(&output, 10, key, value,
true);
135 if (!group.
tests.empty() && i + 1 < suite.
groups.size()) {
144 const std::string& path,
bool overwrite) {
145 std::filesystem::path output_path(path);
147 if (!overwrite && std::filesystem::exists(output_path, ec)) {
149 return absl::AlreadyExistsError(
150 absl::StrCat(
"Test suite file already exists: ", path));
154 std::filesystem::path parent = output_path.parent_path();
155 if (!parent.empty()) {
156 std::filesystem::create_directories(parent, ec);
158 return absl::InternalError(absl::StrCat(
159 "Failed to create directories for ", path,
": ", ec.message()));
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));
174 return absl::InternalError(
175 absl::StrCat(
"Failed to write test suite to ", path));
177 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)
Main namespace for the application.
std::vector< TestCaseDefinition > tests
std::vector< std::string > depends_on
std::vector< TestGroupDefinition > groups