51 if (parser.
GetString(
"min-d6-track-rooms").has_value()) {
52 auto int_or = parser.
GetInt(
"min-d6-track-rooms");
54 return absl::InvalidArgumentError(
55 "--min-d6-track-rooms: value must be a non-negative integer");
58 return absl::InvalidArgumentError(
59 "--min-d6-track-rooms: value must be >= 0");
67 rp.has_value() && !rp->empty()) {
68 const std::filesystem::path rp_path(*rp);
70 const bool existed_before = std::filesystem::exists(rp_path, ec);
71 std::ofstream probe(*rp, std::ios::out | std::ios::binary | std::ios::app);
72 if (!probe.is_open()) {
73 return absl::PermissionDeniedError(
74 absl::StrFormat(
"oracle-smoke-check: cannot open report file "
79 if (!existed_before) {
80 std::filesystem::remove(rp_path, ec);
83 return absl::OkStatus();
89 const bool strict_readiness = parser.
HasFlag(
"strict-readiness");
92 int min_d6_track_rooms = 0;
93 if (
auto int_or = parser.
GetInt(
"min-d6-track-rooms"); int_or.ok()) {
94 min_d6_track_rooms = *int_or;
107 const auto structural =
109 const bool d4_structural_ok = structural.ok();
115 const bool collision_write_support =
121 bool d4_required_ok =
false;
122 const char* d4_readiness_state =
"skipped";
123 if (collision_write_support) {
129 const auto d4_required =
131 d4_required_ok = d4_required.ok();
132 d4_readiness_state =
"ran";
145 .
Run({
"--rooms=0xA8,0xB8,0xD8,0xDA",
"--include-track-objects",
151 int track_rooms_found = 0;
153 const auto d6_doc = json::parse(d6_out,
nullptr,
false);
154 if (!d6_doc.is_discarded() &&
155 d6_doc.contains(
"Dungeon Minecart Audit")) {
156 for (
const auto& room :
157 d6_doc[
"Dungeon Minecart Audit"].value(
"rooms", json::array())) {
158 if (!room.value(
"track_object_subtypes", json::array()).empty()) {
164 const bool meets_min_track_rooms =
165 (min_d6_track_rooms == 0 || track_rooms_found >= min_d6_track_rooms);
171 const char* d3_readiness_state =
"skipped";
172 if (collision_write_support) {
178 const auto d3_required =
180 d3_ok = d3_required.ok();
181 d3_readiness_state =
"ran";
188 bool overall_ok = d4_structural_ok && d6_ok && meets_min_track_rooms;
189 if (strict_readiness) {
193 if (collision_write_support) {
194 overall_ok = overall_ok && d4_required_ok && d3_ok;
202 report[
"ok"] = overall_ok;
203 report[
"status"] = overall_ok ?
"pass" :
"fail";
204 report[
"strict_readiness"] = strict_readiness;
206 json d4_json = json::object();
207 d4_json[
"structural_ok"] = d4_structural_ok;
208 d4_json[
"required_rooms_check"] = d4_readiness_state;
209 if (collision_write_support) {
210 d4_json[
"required_rooms_ok"] = d4_required_ok;
212 d4_json[
"required_rooms"] = json::array({
"0x25",
"0x27"});
213 json structural_errors = json::array();
214 for (
const auto& err : structural.errors) {
215 structural_errors.push_back({{
"code", err.code}, {
"message", err.message}});
217 d4_json[
"structural_errors"] = std::move(structural_errors);
219 json d6_json = json::object();
220 d6_json[
"ok"] = d6_ok;
221 d6_json[
"rooms"] = json::array({
"0xA8",
"0xB8",
"0xD8",
"0xDA"});
222 d6_json[
"track_rooms_found"] = track_rooms_found;
223 d6_json[
"min_track_rooms"] = min_d6_track_rooms;
224 d6_json[
"meets_min_track_rooms"] = meets_min_track_rooms;
226 json d3_json = json::object();
227 d3_json[
"readiness_check"] = d3_readiness_state;
228 if (collision_write_support) {
229 d3_json[
"ok"] = d3_ok;
231 d3_json[
"required_rooms"] = json::array({
"0x32"});
233 report[
"checks"] = json::object();
234 report[
"checks"][
"d4_zora_temple"] = std::move(d4_json);
235 report[
"checks"][
"d6_goron_mines"] = std::move(d6_json);
236 report[
"checks"][
"d3_kalyxo_castle"] = std::move(d3_json);
242 formatter.
AddField(
"ok", overall_ok);
244 overall_ok ? std::string(
"pass") : std::string(
"fail"));
245 formatter.
AddField(
"strict_readiness", strict_readiness);
250 formatter.
AddField(
"structural_ok", d4_structural_ok);
251 formatter.
AddField(
"required_rooms_check",
252 std::string(d4_readiness_state));
253 if (collision_write_support) {
254 formatter.
AddField(
"required_rooms_ok", d4_required_ok);
260 formatter.
AddField(
"track_rooms_found", track_rooms_found);
261 formatter.
AddField(
"min_track_rooms", min_d6_track_rooms);
262 formatter.
AddField(
"meets_min_track_rooms", meets_min_track_rooms);
266 formatter.
AddField(
"readiness_check", std::string(d3_readiness_state));
267 if (collision_write_support) {
278 if (
auto rp = parser.
GetString(
"report");
279 rp.has_value() && !rp->empty()) {
280 std::ofstream report_file(
281 *rp, std::ios::out | std::ios::binary | std::ios::trunc);
282 if (!report_file.is_open()) {
283 return absl::PermissionDeniedError(absl::StrFormat(
284 "oracle-smoke-check: cannot open report file: %s", *rp));
286 report_file << report.dump(2) <<
"\n";
287 if (!report_file.good()) {
288 return absl::InternalError(absl::StrFormat(
289 "oracle-smoke-check: failed writing report: %s", *rp));
297 const bool structural_failed =
298 !d4_structural_ok || !d6_ok || !meets_min_track_rooms;
299 return absl::FailedPreconditionError(absl::StrFormat(
300 "oracle-smoke-check failed %s",
301 structural_failed ?
"(structural)" :
"(strict-readiness)"));
303 return absl::OkStatus();