5 changed files with 190 additions and 43 deletions
@ -0,0 +1,120 @@
|
||||
#include <gtest/gtest.h> |
||||
#include <random> |
||||
|
||||
#include "engine/random.hpp" |
||||
#include "items.h" |
||||
#include "player.h" |
||||
#include "spells.h" |
||||
#include "utils/str_cat.hpp" |
||||
|
||||
namespace devilution { |
||||
namespace { |
||||
|
||||
class ItemsTest : public ::testing::Test { |
||||
public: |
||||
void SetUp() override |
||||
{ |
||||
Players.resize(1); |
||||
MyPlayer = &Players[0]; |
||||
for (auto &flag : UniqueItemFlags) |
||||
flag = false; |
||||
} |
||||
|
||||
static void SetUpTestSuite() |
||||
{ |
||||
LoadItemData(); |
||||
LoadSpellData(); |
||||
gbIsSpawn = false; |
||||
gbIsMultiplayer = false; // to also test UniqueItemFlags
|
||||
} |
||||
}; |
||||
|
||||
void GenerateAllUniques(bool hellfire, const size_t expectedUniques) |
||||
{ |
||||
gbIsHellfire = hellfire; |
||||
|
||||
std::mt19937 betterRng; |
||||
std::uniform_int_distribution<uint32_t> dist(0, std::numeric_limits<uint32_t>::max()); |
||||
|
||||
// Get seed for test run from time. If a test run fails, remember the seed and hardcode it here.
|
||||
uint32_t testRunSeed = static_cast<uint32_t>(time(nullptr)); |
||||
betterRng.seed(testRunSeed); |
||||
|
||||
std::set<int> foundUniques; |
||||
|
||||
constexpr int max_iterations = 1000000; |
||||
int iteration = 0; |
||||
|
||||
for (int uniqueIndex = 0, n = static_cast<int>(UniqueItems.size()); uniqueIndex < n; ++uniqueIndex) { |
||||
|
||||
if (!IsUniqueAvailable(uniqueIndex)) |
||||
continue; |
||||
|
||||
if (foundUniques.contains(uniqueIndex)) |
||||
continue; |
||||
|
||||
auto &uniqueItem = UniqueItems[uniqueIndex]; |
||||
|
||||
_item_indexes uniqueBaseIndex = IDI_GOLD; |
||||
for (std::underlying_type_t<_item_indexes> j = IDI_GOLD; j <= IDI_LAST; j++) { |
||||
if (!IsItemAvailable(j)) |
||||
continue; |
||||
if (AllItemsList[j].iItemId != uniqueItem.UIItemId) |
||||
continue; |
||||
if (AllItemsList[j].iRnd != IDROP_NEVER) |
||||
uniqueBaseIndex = static_cast<_item_indexes>(j); |
||||
break; |
||||
} |
||||
|
||||
if (uniqueBaseIndex == IDI_GOLD) |
||||
continue; // Unique not dropable (Quest-Unique)
|
||||
|
||||
auto &baseItemData = AllItemsList[static_cast<size_t>(uniqueBaseIndex)]; |
||||
|
||||
while (true) { |
||||
iteration += 1; |
||||
|
||||
if (iteration > max_iterations) |
||||
FAIL() << StrCat("Item ", uniqueItem.UIName, " not found in ", max_iterations, " tries with test run seed ", testRunSeed); |
||||
|
||||
Item testItem = {}; |
||||
testItem._iMiscId = baseItemData.iMiscId; |
||||
std::uniform_int_distribution<int32_t> dist(0, INT_MAX); |
||||
SetRndSeed(dist(betterRng)); |
||||
|
||||
int targetLvl = uniqueItem.UIMinLvl; |
||||
int uper = 1; |
||||
if (uniqueItem.UIMinLvl - 4 > 0) { |
||||
uper = 15; |
||||
targetLvl = uniqueItem.UIMinLvl - 4; |
||||
} |
||||
|
||||
SetupAllItems(*MyPlayer, testItem, uniqueBaseIndex, AdvanceRndSeed(), targetLvl, uper, true, false); |
||||
TryRandomUniqueItem(testItem, uniqueBaseIndex, targetLvl, uper, true, false); |
||||
SetupItem(testItem); |
||||
|
||||
if (testItem._iMagical != ITEM_QUALITY_UNIQUE) |
||||
continue; |
||||
|
||||
foundUniques.insert(testItem._iUid); |
||||
|
||||
if (testItem._iUid == uniqueIndex) |
||||
break; |
||||
} |
||||
} |
||||
|
||||
EXPECT_EQ(foundUniques.size(), expectedUniques) << StrCat("test run seed ", testRunSeed); |
||||
} |
||||
|
||||
TEST_F(ItemsTest, AllDiabloUniquesCanDrop) |
||||
{ |
||||
GenerateAllUniques(false, 79); |
||||
} |
||||
|
||||
TEST_F(ItemsTest, AllHellfireUniquesCanDrop) |
||||
{ |
||||
GenerateAllUniques(true, 99); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace devilution
|
||||
Loading…
Reference in new issue