333209fa-0481-4a47-8399-e46cdc73ea81

This commit is contained in:
cdemeyer-teachx
2025-08-14 23:11:46 +00:00
parent 656aada919
commit 56c1baae2a
8 changed files with 1757 additions and 12 deletions

View 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