feat(ugc): models with no bricks are "empty", not failed

A model with no bricks has nothing to make: it gets its own state
(is_optimized = 3), isn't counted as a failure or retried, shows as Empty
on the UGC page (with its own filter and count), in the status and in
Prometheus, and its downloads answer 404 like HKX. State names come from
the enum (magic_enum). Migrations dlu/mysql/87 and dlu/sqlite/70 move the
rows that failed only because they had no bricks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-27 14:52:03 -05:00
parent b4fa732047
commit 5de59b99ef
17 changed files with 90 additions and 29 deletions

View File

@@ -1,3 +1,4 @@
add_executable(dUgcTests "UgcTests.cpp")
target_include_directories(dUgcTests PRIVATE "${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables")
target_link_libraries(dUgcTests ${COMMON_LIBRARIES} dUgc GTest::gtest_main)
gtest_discover_tests(dUgcTests)

View File

@@ -13,6 +13,7 @@
#include "UgcFormats.h"
#include "UgcModel.h"
#include "UgcJobs.h"
#include "IUgc.h"
#include "UgcIconParams.h"
#include "UgcKeys.h"
#include "UgcModular.h"
@@ -709,3 +710,28 @@ TEST(UgcIconParams, OneListDrivesEverything) {
shifted.offsetX = 0.25f;
EXPECT_NEAR(centroid(UgcRender::RenderIcon(model, shifted)) - centroid(UgcRender::RenderIcon(model, plain)), 16.0, 1.0);
}
TEST(UgcJobs, ModelsWithoutBricksAreEmptyNotFailed) {
UgcBricks::BrickLibrary library(MakeRes(), 0);
UgcJobs::Settings settings;
const auto empty = UgcJobs::ProcessModel(R"(<?xml version="1.0"?><LXFML versionMajor="5"><Meta/><Bricks/></LXFML>)", library, settings);
EXPECT_FALSE(empty.ok);
EXPECT_TRUE(empty.empty);
EXPECT_TRUE(UgcModel::HasNoBricks(R"(<LXFML versionMajor="5"><Bricks/></LXFML>)"));
// Broken LXFML, or bricks without geometry, are failures
EXPECT_FALSE(UgcJobs::ProcessModel("<LXFML><nope", library, settings).empty);
const auto missing = UgcJobs::ProcessModel(R"(<LXFML versionMajor="5"><Bricks><Brick><Part designID="9999" materials="1">
<Bone transformation="1,0,0,0,1,0,0,0,1,0,0,0"/></Part></Brick></Bricks></LXFML>)", library, settings);
EXPECT_FALSE(missing.ok);
EXPECT_FALSE(missing.empty);
EXPECT_FALSE(UgcModel::HasNoBricks(R"(<LXFML versionMajor="5"><Bricks><Brick><Part designID="9999"/></Brick></Bricks></LXFML>)"));
}
TEST(UgcStates, NamesComeFromTheEnum) {
EXPECT_EQ(IUgc::ProcessStateName(IUgc::eProcessState::EMPTY), "empty");
EXPECT_EQ(IUgc::ProcessStateName(IUgc::eProcessState::FAILED), "failed");
EXPECT_EQ(IUgc::ParseProcessState("empty"), IUgc::eProcessState::EMPTY);
EXPECT_EQ(IUgc::ParseProcessState("pending"), IUgc::eProcessState::PENDING);
EXPECT_FALSE(IUgc::ParseProcessState("nonsense").has_value());
EXPECT_EQ(magic_enum::enum_count<IUgc::eProcessState>(), 4u);
}