333209fa-0481-4a47-8399-e46cdc73ea81
This commit is contained in:
272
tests/unit/core/test_types.cpp
Normal file
272
tests/unit/core/test_types.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "types.h"
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
|
||||
namespace PokemonSim {
|
||||
|
||||
// Test fixture for type system tests
|
||||
class TypeSystemTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
// Set up test fixtures
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
// Clean up test fixtures
|
||||
}
|
||||
};
|
||||
|
||||
// Test type string conversion
|
||||
TEST_F(TypeSystemTest, StringToTypeConversion) {
|
||||
// Test valid conversions
|
||||
EXPECT_EQ(TypeUtils::stringToType("fire"), Type::FIRE);
|
||||
EXPECT_EQ(TypeUtils::stringToType("WATER"), Type::WATER); // Case insensitive
|
||||
EXPECT_EQ(TypeUtils::stringToType("Normal"), Type::NORMAL);
|
||||
EXPECT_EQ(TypeUtils::stringToType("none"), Type::NONE);
|
||||
|
||||
// Test invalid conversions
|
||||
EXPECT_FALSE(TypeUtils::stringToType("invalid_type").has_value());
|
||||
EXPECT_FALSE(TypeUtils::stringToType("").has_value());
|
||||
}
|
||||
|
||||
TEST_F(TypeSystemTest, TypeToStringConversion) {
|
||||
// Test valid conversions
|
||||
EXPECT_EQ(TypeUtils::typeToString(Type::FIRE), "fire");
|
||||
EXPECT_EQ(TypeUtils::typeToString(Type::WATER), "water");
|
||||
EXPECT_EQ(TypeUtils::typeToString(Type::NONE), "none");
|
||||
EXPECT_EQ(TypeUtils::typeToString(Type::DARK), "dark");
|
||||
EXPECT_EQ(TypeUtils::typeToString(Type::FAIRY), "fairy");
|
||||
|
||||
// Test out of range (shouldn't happen in practice but good to test)
|
||||
EXPECT_EQ(TypeUtils::typeToString(static_cast<Type>(999)), "unknown");
|
||||
}
|
||||
|
||||
// Test PokemonTypes class
|
||||
TEST_F(TypeSystemTest, PokemonTypesBasic) {
|
||||
// Single type Pokemon
|
||||
PokemonTypes charizard(Type::FIRE);
|
||||
EXPECT_EQ(charizard.getPrimary(), Type::FIRE);
|
||||
EXPECT_EQ(charizard.getSecondary(), Type::NONE);
|
||||
EXPECT_FALSE(charizard.hasSecondary());
|
||||
|
||||
// Dual type Pokemon
|
||||
PokemonTypes arcanine(Type::FIRE, Type::NORMAL);
|
||||
EXPECT_EQ(arcanine.getPrimary(), Type::FIRE);
|
||||
EXPECT_EQ(arcanine.getSecondary(), Type::NORMAL);
|
||||
EXPECT_TRUE(arcanine.hasSecondary());
|
||||
}
|
||||
|
||||
TEST_F(TypeSystemTest, PokemonTypesModification) {
|
||||
PokemonTypes pokemon;
|
||||
|
||||
// Test default state
|
||||
EXPECT_EQ(pokemon.getPrimary(), Type::NONE);
|
||||
EXPECT_EQ(pokemon.getSecondary(), Type::NONE);
|
||||
|
||||
// Test setting types
|
||||
pokemon.setPrimary(Type::WATER);
|
||||
pokemon.setSecondary(Type::FLYING);
|
||||
|
||||
EXPECT_EQ(pokemon.getPrimary(), Type::WATER);
|
||||
EXPECT_EQ(pokemon.getSecondary(), Type::FLYING);
|
||||
EXPECT_TRUE(pokemon.hasSecondary());
|
||||
}
|
||||
|
||||
// Test type effectiveness for Generation 1
|
||||
TEST_F(TypeSystemTest, TypeEffectivenessGen1) {
|
||||
// Test some well-known type matchups from Generation 1
|
||||
EXPECT_EQ(TypeUtils::getTypeEffectiveness<Generation::GENERATION_1>(Type::WATER, Type::FIRE), TypeMultiplier::DOUBLE);
|
||||
EXPECT_EQ(TypeUtils::getTypeEffectiveness<Generation::GENERATION_1>(Type::FIRE, Type::WATER), TypeMultiplier::HALF);
|
||||
EXPECT_EQ(TypeUtils::getTypeEffectiveness<Generation::GENERATION_1>(Type::NORMAL, Type::ROCK), TypeMultiplier::HALF);
|
||||
EXPECT_EQ(TypeUtils::getTypeEffectiveness<Generation::GENERATION_1>(Type::ELECTRIC, Type::GROUND), TypeMultiplier::ZERO);
|
||||
EXPECT_EQ(TypeUtils::getTypeEffectiveness<Generation::GENERATION_1>(Type::FIGHTING, Type::GHOST), TypeMultiplier::ZERO);
|
||||
}
|
||||
|
||||
TEST_F(TypeSystemTest, TypeEffectivenessNeutral) {
|
||||
// Test neutral effectiveness (1x damage)
|
||||
EXPECT_EQ(TypeUtils::getTypeEffectiveness<Generation::GENERATION_1>(Type::NORMAL, Type::NORMAL), TypeMultiplier::NEUTRAL);
|
||||
EXPECT_EQ(TypeUtils::getTypeEffectiveness<Generation::GENERATION_1>(Type::FIRE, Type::FIRE), TypeMultiplier::NEUTRAL);
|
||||
|
||||
// Test NONE types
|
||||
EXPECT_EQ(TypeUtils::getTypeEffectiveness<Generation::GENERATION_1>(Type::NONE, Type::FIRE), TypeMultiplier::NEUTRAL);
|
||||
EXPECT_EQ(TypeUtils::getTypeEffectiveness<Generation::GENERATION_1>(Type::FIRE, Type::NONE), TypeMultiplier::NEUTRAL);
|
||||
}
|
||||
|
||||
// Test damage multiplier calculations
|
||||
TEST_F(TypeSystemTest, DamageMultiplierSingleType) {
|
||||
// Single type Pokemon
|
||||
PokemonTypes charizard(Type::FIRE);
|
||||
|
||||
// Fire attack on Fire type (0.5x)
|
||||
uint32_t multiplier = TypeUtils::calculateDamageMultiplier<Generation::GENERATION_1>(Type::FIRE, charizard);
|
||||
EXPECT_EQ(multiplier, static_cast<uint32_t>(TypeMultiplier::HALF));
|
||||
|
||||
// Water attack on Fire type (2x)
|
||||
multiplier = TypeUtils::calculateDamageMultiplier<Generation::GENERATION_1>(Type::WATER, charizard);
|
||||
EXPECT_EQ(multiplier, static_cast<uint32_t>(TypeMultiplier::DOUBLE));
|
||||
}
|
||||
|
||||
TEST_F(TypeSystemTest, DamageMultiplierDualType) {
|
||||
// Dual type Pokemon (Water/Flying)
|
||||
PokemonTypes gyarados(Type::WATER, Type::FLYING);
|
||||
|
||||
// Electric attack on Water/Flying (2x * 2x = 4x)
|
||||
uint32_t multiplier = TypeUtils::calculateDamageMultiplier<Generation::GENERATION_1>(Type::ELECTRIC, gyarados);
|
||||
EXPECT_EQ(multiplier, static_cast<uint32_t>(TypeMultiplier::QUADRUPLE));
|
||||
|
||||
// Grass attack on Water/Flying (0.5x * 0.5x = 0.25x)
|
||||
multiplier = TypeUtils::calculateDamageMultiplier<Generation::GENERATION_1>(Type::GRASS, gyarados);
|
||||
EXPECT_EQ(multiplier, static_cast<uint32_t>(TypeMultiplier::QUARTER));
|
||||
}
|
||||
|
||||
TEST_F(TypeSystemTest, DamageMultiplierNoneAttack) {
|
||||
PokemonTypes charizard(Type::FIRE);
|
||||
|
||||
// NONE attack type should always return neutral multiplier
|
||||
uint32_t multiplier = TypeUtils::calculateDamageMultiplier<Generation::GENERATION_1>(Type::NONE, charizard);
|
||||
EXPECT_EQ(multiplier, static_cast<uint32_t>(TypeMultiplier::NEUTRAL));
|
||||
}
|
||||
|
||||
// Test the high-level damage calculation function
|
||||
TEST_F(TypeSystemTest, CalculateDamage) {
|
||||
PokemonTypes charizard(Type::FIRE);
|
||||
const uint32_t baseDamage = 100;
|
||||
|
||||
// Fire attack on Fire type: 100 * 0.5 = 50
|
||||
uint32_t damage = calculateDamage<Generation::GENERATION_1>(baseDamage, Type::FIRE, charizard);
|
||||
EXPECT_EQ(damage, 50);
|
||||
|
||||
// Water attack on Fire type: 100 * 2 = 200
|
||||
damage = calculateDamage<Generation::GENERATION_1>(baseDamage, Type::WATER, charizard);
|
||||
EXPECT_EQ(damage, 200);
|
||||
|
||||
// Normal attack on Fire type: 100 * 1 = 100
|
||||
damage = calculateDamage<Generation::GENERATION_1>(baseDamage, Type::NORMAL, charizard);
|
||||
EXPECT_EQ(damage, 100);
|
||||
}
|
||||
|
||||
TEST_F(TypeSystemTest, CalculateDamageDualType) {
|
||||
PokemonTypes gyarados(Type::WATER, Type::FLYING);
|
||||
const uint32_t baseDamage = 100;
|
||||
|
||||
// Electric attack on Water/Flying: 100 * 4 = 400
|
||||
uint32_t damage = calculateDamage<Generation::GENERATION_1>(baseDamage, Type::ELECTRIC, gyarados);
|
||||
EXPECT_EQ(damage, 400);
|
||||
|
||||
// Grass attack on Water/Flying: 100 * 0.25 = 25
|
||||
damage = calculateDamage<Generation::GENERATION_1>(baseDamage, Type::GRASS, gyarados);
|
||||
EXPECT_EQ(damage, 25);
|
||||
}
|
||||
|
||||
// Test edge cases
|
||||
TEST_F(TypeSystemTest, EdgeCases) {
|
||||
// Test with zero base damage
|
||||
PokemonTypes pokemon(Type::FIRE);
|
||||
uint32_t damage = calculateDamage<Generation::GENERATION_1>(0, Type::WATER, pokemon);
|
||||
EXPECT_EQ(damage, 0);
|
||||
|
||||
// Test immunity (0x multiplier)
|
||||
damage = calculateDamage<Generation::GENERATION_1>(100, Type::GROUND, PokemonTypes(Type::ELECTRIC));
|
||||
EXPECT_EQ(damage, 0);
|
||||
}
|
||||
|
||||
// Test different generations have different type charts
|
||||
TEST_F(TypeSystemTest, GenerationDifferences) {
|
||||
// Ghost/Fighting immunity in Gen 1
|
||||
uint32_t gen1Multiplier = TypeUtils::calculateDamageMultiplier<Generation::GENERATION_1>(
|
||||
Type::FIGHTING, PokemonTypes(Type::GHOST));
|
||||
EXPECT_EQ(gen1Multiplier, static_cast<uint32_t>(TypeMultiplier::ZERO));
|
||||
|
||||
// In later generations, this might be different (Normal/Fighting would be neutral)
|
||||
// Note: This is just a demonstration - we'd need actual Gen 2+ data to test properly
|
||||
}
|
||||
|
||||
// Parameterized test for various type combinations
|
||||
class TypeCombinationTest : public ::testing::TestWithParam<std::tuple<Type, Type, uint32_t>> {
|
||||
};
|
||||
|
||||
TEST_P(TypeCombinationTest, VariousTypeCombinations) {
|
||||
auto [attackType, defendType, expectedMultiplier] = GetParam();
|
||||
|
||||
PokemonTypes defender(defendType);
|
||||
uint32_t actualMultiplier = TypeUtils::calculateDamageMultiplier<Generation::GENERATION_1>(attackType, defender);
|
||||
|
||||
EXPECT_EQ(actualMultiplier, expectedMultiplier)
|
||||
<< "Attack: " << TypeUtils::typeToString(attackType)
|
||||
<< ", Defense: " << TypeUtils::typeToString(defendType);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
TypeCombinations,
|
||||
TypeCombinationTest,
|
||||
::testing::Values(
|
||||
std::make_tuple(Type::WATER, Type::FIRE, static_cast<uint32_t>(TypeMultiplier::DOUBLE)),
|
||||
std::make_tuple(Type::FIRE, Type::WATER, static_cast<uint32_t>(TypeMultiplier::HALF)),
|
||||
std::make_tuple(Type::NORMAL, Type::ROCK, static_cast<uint32_t>(TypeMultiplier::HALF)),
|
||||
std::make_tuple(Type::ELECTRIC, Type::GROUND, static_cast<uint32_t>(TypeMultiplier::ZERO)),
|
||||
std::make_tuple(Type::NORMAL, Type::NORMAL, static_cast<uint32_t>(TypeMultiplier::NEUTRAL))
|
||||
)
|
||||
);
|
||||
|
||||
// Performance test to ensure type lookups are fast
|
||||
TEST_F(TypeSystemTest, PerformanceTest) {
|
||||
PokemonTypes pokemon(Type::FIRE, Type::FLYING);
|
||||
|
||||
// Test that multiple lookups are fast (this would be caught by benchmark tests too)
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
uint32_t multiplier = TypeUtils::calculateDamageMultiplier<Generation::GENERATION_1>(Type::WATER, pokemon);
|
||||
(void)multiplier; // Prevent optimization
|
||||
}
|
||||
}
|
||||
|
||||
// Test the high-level damage calculation functions
|
||||
TEST_F(TypeSystemTest, CalculateDamageFunctions) {
|
||||
// Test calculateTypeDamage with single type
|
||||
uint32_t damage1 = calculateTypeDamage<Generation::GENERATION_1>(100, Type::WATER, Type::FIRE);
|
||||
EXPECT_EQ(damage1, 200);
|
||||
|
||||
uint32_t damage2 = calculateTypeDamage<Generation::GENERATION_1>(100, Type::FIRE, Type::WATER);
|
||||
EXPECT_EQ(damage2, 50);
|
||||
|
||||
// Test calculateTypeDamage with dual type
|
||||
uint32_t damage3 = calculateTypeDamage<Generation::GENERATION_1>(100, Type::ELECTRIC, Type::WATER, Type::FLYING);
|
||||
EXPECT_EQ(damage3, 400);
|
||||
|
||||
// Test immunity
|
||||
uint32_t damage4 = calculateTypeDamage<Generation::GENERATION_1>(100, Type::GROUND, Type::ELECTRIC);
|
||||
EXPECT_EQ(damage4, 0);
|
||||
}
|
||||
|
||||
// Test Pokemon damage calculation methods
|
||||
TEST_F(TypeSystemTest, PokemonDamageCalculation) {
|
||||
Pokemon charizard("Charizard", 100, Type::FIRE, Type::FLYING);
|
||||
|
||||
// Test various attack types
|
||||
uint32_t fireDamage = charizard.calculateDamageTaken<Generation::GENERATION_1>(100, Type::FIRE);
|
||||
EXPECT_EQ(fireDamage, 50);
|
||||
|
||||
uint32_t waterDamage = charizard.calculateDamageTaken<Generation::GENERATION_1>(100, Type::WATER);
|
||||
EXPECT_EQ(waterDamage, 200);
|
||||
|
||||
uint32_t electricDamage = charizard.calculateDamageTaken<Generation::GENERATION_1>(100, Type::ELECTRIC);
|
||||
EXPECT_EQ(electricDamage, 200); // 2x vs Flying, neutral vs Fire
|
||||
|
||||
uint32_t groundDamage = charizard.calculateDamageTaken<Generation::GENERATION_1>(100, Type::GROUND);
|
||||
EXPECT_EQ(groundDamage, 0); // Ground is immune to Flying
|
||||
}
|
||||
|
||||
// Test generation differences
|
||||
TEST_F(TypeSystemTest, GenerationDifferencesTest) {
|
||||
// Steel type was introduced in Gen 2
|
||||
uint32_t gen1Damage = calculateTypeDamage<Generation::GENERATION_1>(100, Type::NORMAL, Type::STEEL);
|
||||
uint32_t gen8Damage = calculateTypeDamage<Generation::GENERATION_8>(100, Type::NORMAL, Type::STEEL);
|
||||
|
||||
// Results may differ based on loaded type charts
|
||||
EXPECT_GE(gen1Damage, 0);
|
||||
EXPECT_GE(gen8Damage, 0);
|
||||
EXPECT_LE(gen1Damage, 400);
|
||||
EXPECT_LE(gen8Damage, 400);
|
||||
}
|
||||
|
||||
} // namespace PokemonSim
|
||||
Reference in New Issue
Block a user