Advertisement
malice936

PluginTest.h

Apr 13th, 2025
266
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 17.04 KB | Gaming | 0 0
  1. #pragma once
  2.  
  3. #include "CoreMinimal.h"         // Core Unreal Engine types and utilities
  4. #include "Misc/AutomationTest.h" // Automation test framework for Unreal Engine
  5. #include "Plugin.h"              // Custom plugin-related classes (FPlugin, UPluginHelper, EGameID)
  6. #include "HAL/FileManager.h"     // File management utilities (IFileManager)
  7. #include "Misc/Paths.h"          // Path manipulation utilities (FPaths)
  8. #include "Misc/Parse.h"          // Parsing utilities (used for FString::FromInt)
  9.  
  10. // Enable automation tests only if WITH_DEV_AUTOMATION_TESTS is defined
  11. #if WITH_DEV_AUTOMATION_TESTS
  12.  
  13. /*
  14. Macro to declare an automation test for a specific game, generating a test class with Unreal Engine's testing framework.
  15. Parameters:
  16. - GameIDEnum: Enum value from EGameID (e.g., Skyrim, Oblivion) to identify the game.
  17. - GameName: Name of the game as a string (e.g., Skyrim) used in test naming and paths.
  18. */
  19.  
  20. #define DECLARE_PLUGIN_TEST(GameIDEnum, GameName) \
  21. IMPLEMENT_SIMPLE_AUTOMATION_TEST(FPluginTest_##GameName, "PluginTest." #GameName, EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) \
  22. bool FPluginTest_##GameName::RunTest(const FString& Parameters) \
  23. { \
  24.     /* Set the game identifier based on the provided enum (e.g., EGameID::Skyrim). */ \
  25.     EGameID GameID = EGameID::GameIDEnum; \
  26.     /* Convert the game name to a string for use in file paths and test messages. */ \
  27.     FString GameNameStr = TEXT(#GameName); \
  28.     /* Define a temporary directory for test files in Saved/Temp/PluginTest/[GameName]. */ \
  29.     FString TempDir = FPaths::ProjectSavedDir() / TEXT("Temp") / TEXT("PluginTest") / GameNameStr; \
  30.     /* Get the singleton file manager instance for file operations. */ \
  31.     IFileManager& FileManager = IFileManager::Get(); \
  32.     /* Create the temporary directory, including parent directories if needed. */ \
  33.     FileManager.MakeDirectory(*TempDir, true); \
  34. \
  35.     /* Source File Paths - Define the source directory for test files, located in Content/TestFiles/[GameName]. */ \
  36.     FString SourceDir = FPaths::ProjectContentDir() / TEXT("TestFiles") / GameNameStr; \
  37.     /* Path to the source Blank.esm file, a master plugin file. */ \
  38.     FString BlankEsmSource = FPaths::Combine(SourceDir, TEXT("Blank.esm")); \
  39.     /* Path to the source Blank - Master Dependent.esm, a master file dependent on Blank.esm. */ \
  40.     FString BlankMasterDependentEsmSource = FPaths::Combine(SourceDir, TEXT("Blank - Master Dependent.esm")); \
  41.     /* Path to the source Blank.esp file, a non-master plugin file. */ \
  42.     FString BlankEspSource = FPaths::Combine(SourceDir, TEXT("Blank.esp")); \
  43.     /* Path to the source Blank.bsa file, a Skyrim-specific archive file. */ \
  44.     FString BlankBsaSource = FPaths::Combine(SourceDir, TEXT("Blank.bsa")); /* Skyrim only */ \
  45. \
  46.     /* Temporary Test File Paths - Path to a missing plugin file for testing load failures. */ \
  47.     FString MissingPluginPath = FPaths::Combine(TempDir, TEXT("Blank.missing.esm")); \
  48.     /* Path to an empty file for testing invalid plugin loading. */ \
  49.     FString EmptyFilePath = FPaths::Combine(TempDir, TEXT("EmptyFile.esm")); \
  50.     /* Path to an invalid plugin file with non-plugin content. */ \
  51.     FString InvalidPluginPath = FPaths::Combine(TempDir, TEXT("NotAPlugin.esm")); \
  52.     /* Path to a plugin file with a non-ASCII name for testing Unicode support. */ \
  53.     FString NonAsciiPluginPath = FPaths::Combine(TempDir, TEXT("Русский.esm")); \
  54.     /* Path to the temporary copy of Blank.esm. */ \
  55.     FString BlankEsmPath = FPaths::Combine(TempDir, TEXT("Blank.esm")); \
  56.     /* Path to the temporary copy of Blank - Master Dependent.esm. */ \
  57.     FString BlankMasterDependentEsmPath = FPaths::Combine(TempDir, TEXT("Blank - Master Dependent.esm")); \
  58.     /* Path to the temporary copy of Blank.esp. */ \
  59.     FString BlankEspPath = FPaths::Combine(TempDir, TEXT("Blank.esp")); \
  60.     /* Path to the temporary copy of Blank.bsa (Skyrim only). */ \
  61.     FString BlankBsaPath = FPaths::Combine(TempDir, TEXT("Blank.bsa")); /* Skyrim only */ \
  62.     /* Path to a ghosted version of Blank.esm for testing ghost file handling. */ \
  63.     FString GhostedBlankEsmPath = FPaths::Combine(TempDir, TEXT("Blank.esm.ghost")); \
  64. \
  65.     /* Setup: Verify Source Files and Prepare Temporary Files - Verify that the source Blank.esm exists in the source directory. */ \
  66.     TestTrue(FString::Printf(TEXT("Source Blank.esm exists for %s"), *GameNameStr), FileManager.FileExists(*BlankEsmSource)); \
  67.     /* Verify that the source Blank - Master Dependent.esm exists. */ \
  68.     TestTrue(FString::Printf(TEXT("Source Blank - Master Dependent.esm exists for %s"), *GameNameStr), FileManager.FileExists(*BlankMasterDependentEsmSource)); \
  69.     /* Verify that the source Blank.esp exists. */ \
  70.     TestTrue(FString::Printf(TEXT("Source Blank.esp exists for %s"), *GameNameStr), FileManager.FileExists(*BlankEspSource)); \
  71.     /* For Skyrim, verify that the source Blank.bsa exists. */ \
  72.     if (GameID == EGameID::Skyrim) \
  73.     { \
  74.         TestTrue(FString::Printf(TEXT("Source Blank.bsa exists for Skyrim")), FileManager.FileExists(*BlankBsaSource)); \
  75.     } \
  76.     /* Ensure the missing plugin file does not exist initially. */ \
  77.     TestFalse(FString::Printf(TEXT("Missing plugin does not exist for %s"), *GameNameStr), FileManager.FileExists(*MissingPluginPath)); \
  78. \
  79.     /* Copy source plugin files to the temporary directory for testing. */ \
  80.     FileManager.Copy(*BlankEsmPath, *BlankEsmSource); \
  81.     FileManager.Copy(*BlankMasterDependentEsmPath, *BlankMasterDependentEsmSource); \
  82.     FileManager.Copy(*BlankEspPath, *BlankEspSource); \
  83.     if (GameID == EGameID::Skyrim) \
  84.     { \
  85.         FileManager.Copy(*BlankBsaPath, *BlankBsaSource); \
  86.     } \
  87. \
  88.     /* Create an empty file to simulate an invalid plugin. */ \
  89.     TUniquePtr<FArchive> EmptyFileArchive(FileManager.CreateFileWriter(*EmptyFilePath)); \
  90.     EmptyFileArchive->Close(); \
  91.     TestTrue(FString::Printf(TEXT("EmptyFile.esm created for %s"), *GameNameStr), FileManager.FileExists(*EmptyFilePath)); \
  92. \
  93.     /* Create an invalid plugin file with dummy content for testing. */ \
  94.     TUniquePtr<FArchive> InvalidPluginArchive(FileManager.CreateFileWriter(*InvalidPluginPath)); \
  95.     FString InvalidContent = TEXT("This isn't a valid plugin file."); \
  96.     InvalidPluginArchive->Serialize(TCHAR_TO_ANSI(*InvalidContent), InvalidContent.Len()); \
  97.     InvalidPluginArchive->Close(); \
  98.     TestTrue(FString::Printf(TEXT("NotAPlugin.esm created for %s"), *GameNameStr), FileManager.FileExists(*InvalidPluginPath)); \
  99. \
  100.     /* Copy Blank.esm to a non-ASCII named file to test Unicode handling. */ \
  101.     FileManager.Copy(*NonAsciiPluginPath, *BlankEsmPath); \
  102.     TestTrue(FString::Printf(TEXT("Русский.esm created for %s"), *GameNameStr), FileManager.FileExists(*NonAsciiPluginPath)); \
  103. \
  104.     /* Test Function Definitions - Test loading a missing plugin, expecting it to fail. */ \
  105.     auto TestLoadMissingPlugin = [&](FPlugin& Plugin) { \
  106.         bool bLoadFailed = !UPluginHelper::LoadPlugin(Plugin, MissingPluginPath, GameID, false); \
  107.         TestTrue(FString::Printf(TEXT("Load missing plugin fails for %s"), *GameNameStr), bLoadFailed); \
  108.     }; \
  109. \
  110.     /* Test loading a valid plugin, expecting it to succeed. */ \
  111.     auto TestLoadValidPlugin = [&](FPlugin& Plugin, const FString& FilePath) { \
  112.         bool bLoaded = UPluginHelper::LoadPlugin(Plugin, FilePath, GameID, false); \
  113.         TestTrue(FString::Printf(TEXT("Load valid plugin %s succeeds for %s"), *FPaths::GetBaseFilename(FilePath), *GameNameStr), bLoaded); \
  114.     }; \
  115. \
  116.     /* Test loading an invalid plugin, expecting it to fail, with a custom description. */ \
  117.     auto TestLoadInvalidPlugin = [&](FPlugin& Plugin, const FString& FilePath, const FString& TestDesc) { \
  118.         bool bLoadFailed = !UPluginHelper::LoadPlugin(Plugin, FilePath, GameID, false); \
  119.         TestTrue(FString::Printf(TEXT("Load %s fails for %s"), *TestDesc, *GameNameStr), bLoadFailed); \
  120.     }; \
  121. \
  122.     /* Test if a plugin file is valid, comparing the result to an expected value. */ \
  123.     auto TestIsValid = [&](const FString& FilePath, bool bExpectedValid, bool bHeaderOnly = false) { \
  124.         bool bIsValid = UPluginHelper::IsValidPlugin(FilePath, GameID, bHeaderOnly); \
  125.         TestEqual(FString::Printf(TEXT("IsValid %s returns %s for %s (HeaderOnly=%d)"), *FPaths::GetBaseFilename(FilePath), bExpectedValid ? TEXT("true") : TEXT("false"), *GameNameStr, bHeaderOnly), bIsValid, bExpectedValid); \
  126.     }; \
  127. \
  128.     /* Test if the plugin's name matches the expected name after loading. */ \
  129.     auto TestPluginName = [&](FPlugin& Plugin, const FString& FilePath, const FString& ExpectedName) { \
  130.         TestLoadValidPlugin(Plugin, FilePath); \
  131.         TestEqual(FString::Printf(TEXT("Plugin name matches %s for %s"), *ExpectedName, *GameNameStr), UPluginHelper::GetName(Plugin), ExpectedName); \
  132.     }; \
  133. \
  134.     /* Test if the plugin is a master file, comparing to an expected value. */ \
  135.     auto TestIsMasterFile = [&](FPlugin& Plugin, const FString& FilePath, bool bExpectedMaster) { \
  136.         TestLoadValidPlugin(Plugin, FilePath); \
  137.         TestEqual(FString::Printf(TEXT("%s isMasterFile returns %s for %s"), *FPaths::GetBaseFilename(FilePath), bExpectedMaster ? TEXT("true") : TEXT("false"), *GameNameStr), UPluginHelper::IsMasterFile(Plugin), bExpectedMaster); \
  138.     }; \
  139. \
  140.     /* Test the plugin's master dependencies, checking count and specific masters. */ \
  141.     auto TestMasters = [&](FPlugin& Plugin, const FString& FilePath, const TArray<FString>& ExpectedMasters) { \
  142.         TestLoadValidPlugin(Plugin, FilePath); \
  143.         TArray<FString> Masters = UPluginHelper::GetMasters(Plugin); \
  144.         TestEqual(FString::Printf(TEXT("Masters count for %s in %s"), *FPaths::GetBaseFilename(FilePath), *GameNameStr), Masters.Num(), ExpectedMasters.Num()); \
  145.         for (const FString& Master : ExpectedMasters) \
  146.         { \
  147.             TestTrue(FString::Printf(TEXT("Master %s found for %s in %s"), *Master, *FPaths::GetBaseFilename(FilePath), *GameNameStr), Masters.Contains(Master)); \
  148.         } \
  149.     }; \
  150. \
  151.     /* Test the plugin's description, comparing to an expected value. */ \
  152.     auto TestDescription = [&](FPlugin& Plugin, const FString& FilePath, const FString& ExpectedDesc) { \
  153.         TestLoadValidPlugin(Plugin, FilePath); \
  154.         TestEqual(FString::Printf(TEXT("Description for %s in %s"), *FPaths::GetBaseFilename(FilePath), *GameNameStr), UPluginHelper::GetDescription(Plugin), ExpectedDesc); \
  155.     }; \
  156. \
  157.     /* Test the plugin's FormIDs, checking count and specific IDs against expected values. */ \
  158.     auto TestFormIds = [&](FPlugin& Plugin, const FString& FilePath, const TArray<FFormID>& ExpectedFormIds, bool bHeaderOnly = false) { \
  159.         FString FileName = FPaths::GetBaseFilename(FilePath); \
  160.         bool bLoaded = UPluginHelper::LoadPlugin(Plugin, FilePath, GameID, bHeaderOnly); \
  161.         FString LoadTestDesc = FString::Format(TEXT("Load {0} for FormIDs succeeds for {1}"), {FStringFormatArg(FileName), FStringFormatArg(GameNameStr)}); \
  162.         TestTrue(*LoadTestDesc, bLoaded); \
  163.         TArray<FFormID> FormIds = UPluginHelper::GetFormIds(Plugin); \
  164.         FString CountTestDesc = FString::Format(TEXT("FormIDs count for {0} in {1}"), {FStringFormatArg(FileName), FStringFormatArg(GameNameStr)}); \
  165.         TestEqual(*CountTestDesc, FormIds.Num(), ExpectedFormIds.Num()); \
  166.         for (const FFormID& ExpectedId : ExpectedFormIds) \
  167.         { \
  168.             FString ValueStr = FString::FromInt(ExpectedId.Value); \
  169.             FString FormIdTestDesc = FString::Format(TEXT("FormID {0} found for {1} in {2}"), {FStringFormatArg(ValueStr), FStringFormatArg(FileName), FStringFormatArg(GameNameStr)}); \
  170.             TestTrue(*FormIdTestDesc, FormIds.Contains(ExpectedId)); \
  171.         } \
  172.     }; \
  173. \
  174.     /* Test the plugin's record and group count, comparing to an expected value. */ \
  175.     auto TestRecordAndGroupCount = [&](FPlugin& Plugin, const FString& FilePath, int32 ExpectedCount, bool bHeaderOnly = false) { \
  176.         bool bLoaded = UPluginHelper::LoadPlugin(Plugin, FilePath, GameID, bHeaderOnly); \
  177.         TestTrue(FString::Printf(TEXT("Load %s for record count succeeds for %s"), *FPaths::GetBaseFilename(FilePath), *GameNameStr), bLoaded); \
  178.         TestEqual(FString::Printf(TEXT("Record and group count for %s in %s"), *FPaths::GetBaseFilename(FilePath), *GameNameStr), UPluginHelper::GetRecordAndGroupCount(Plugin), ExpectedCount); \
  179.     }; \
  180. \
  181.     /* Execute Tests - Create a plugin instance for running tests. */ \
  182.     FPlugin Plugin; \
  183. \
  184.     /* Test loading a missing plugin file. */ \
  185.     TestLoadMissingPlugin(Plugin); \
  186.     /* Test loading valid plugins (Blank.esm and non-ASCII named plugin). */ \
  187.     TestLoadValidPlugin(Plugin, BlankEsmPath); \
  188.     TestLoadValidPlugin(Plugin, NonAsciiPluginPath); \
  189.     /* Test loading invalid plugins (empty and invalid content files). */ \
  190.     TestLoadInvalidPlugin(Plugin, EmptyFilePath, TEXT("empty file")); \
  191.     TestLoadInvalidPlugin(Plugin, InvalidPluginPath, TEXT("invalid plugin")); \
  192.     /* For Skyrim, test loading the BSA file as an invalid plugin. */ \
  193.     if (GameID == EGameID::Skyrim) \
  194.     { \
  195.         TestLoadInvalidPlugin(Plugin, BlankBsaPath, TEXT("Skyrim BSA")); \
  196.     } \
  197. \
  198.     /* Test plugin validity for Blank.esm (valid) and NotAPlugin.esm (invalid). */ \
  199.     TestIsValid(BlankEsmPath, true); \
  200.     TestIsValid(InvalidPluginPath, false); \
  201.     /* For Skyrim, test Blank.bsa as an invalid plugin. */ \
  202.     if (GameID == EGameID::Skyrim) \
  203.     { \
  204.         TestIsValid(BlankBsaPath, false); \
  205.     } \
  206.     /* Test validity with header-only check. */ \
  207.     TestIsValid(BlankEsmPath, true, true); \
  208.     TestIsValid(InvalidPluginPath, false, true); \
  209.     if (GameID == EGameID::Skyrim) \
  210.     { \
  211.         TestIsValid(BlankBsaPath, false, true); \
  212.     } \
  213. \
  214.     /* Test plugin names for Blank.esm and Blank.esp. */ \
  215.     TestPluginName(Plugin, BlankEsmPath, TEXT("Blank")); \
  216.     TestPluginName(Plugin, BlankEspPath, TEXT("Blank")); \
  217. \
  218.     /* Test master file status for Blank.esm (true) and Blank.esp (false). */ \
  219.     TestIsMasterFile(Plugin, BlankEsmPath, true); \
  220.     TestIsMasterFile(Plugin, BlankEspPath, false); \
  221. \
  222.     /* Test master dependencies: Blank.esm (none), Blank - Master Dependent.esm (Blank.esm). */ \
  223.     TestMasters(Plugin, BlankEsmPath, TArray<FString>()); \
  224.     TestMasters(Plugin, BlankMasterDependentEsmPath, { TEXT("Blank.esm") }); \
  225. \
  226.     /* Test plugin descriptions for various files. */ \
  227.     TestDescription(Plugin, BlankEsmPath, TEXT("v5.0")); \
  228.     TestDescription(Plugin, BlankEspPath, TEXT("€ƒŠ")); \
  229.     TestDescription(Plugin, BlankMasterDependentEsmPath, TEXT("")); \
  230. \
  231.     /* Morrowind-specific test: FormIDs should be empty. */ \
  232.     if (GameID == EGameID::Morrowind) \
  233.     { \
  234.         TestLoadValidPlugin(Plugin, BlankEsmPath); \
  235.         TestTrue(FString::Printf(TEXT("GetFormIds returns empty for Morrowind")), UPluginHelper::GetFormIds(Plugin).Num() == 0); \
  236.     } \
  237.     else \
  238.     { \
  239.         /* Define expected FormIDs for Blank.esm (0xCF0 to 0xCF9). */ \
  240.         TArray<FFormID> BlankEsmFormIds; \
  241.         for (int32 i = 0xCF0; i <= 0xCF9; i++) BlankEsmFormIds.Add(FFormID{i}); \
  242.         TestFormIds(Plugin, BlankEsmPath, BlankEsmFormIds); \
  243. \
  244.         /* Create and test a ghosted version of Blank.esm. */ \
  245.         FileManager.Copy(*GhostedBlankEsmPath, *BlankEsmPath); \
  246.         TestPluginName(Plugin, GhostedBlankEsmPath, TEXT("Blank")); \
  247.         TestFormIds(Plugin, GhostedBlankEsmPath, BlankEsmFormIds); \
  248. \
  249.         /* Define expected FormIDs for Blank - Master Dependent.esm. */ \
  250.         TArray<FFormID> MasterDependentFormIds = { FFormID{static_cast<int32>(0xCF0)}, FFormID{static_cast<int32>(0xCF1)}, FFormID{static_cast<int32>(0xCF2)}, FFormID{static_cast<int32>(0xCF3)}, \
  251.                                                    FFormID{static_cast<int32>(0x01000CEA)}, FFormID{static_cast<int32>(0x01000CEB)}, FFormID{static_cast<int32>(0x01000CEC)}, FFormID{static_cast<int32>(0x01000CED)} }; \
  252.         TestFormIds(Plugin, BlankMasterDependentEsmPath, MasterDependentFormIds); \
  253. \
  254.         /* Test FormIDs with header-only load (expecting none). */ \
  255.         TestFormIds(Plugin, BlankEsmPath, TArray<FFormID>(), true); \
  256.     } \
  257. \
  258.     /* Set expected record and group count based on the game. */ \
  259.     int32 ExpectedCount = (GameID == EGameID::Morrowind) ? 10 : (GameID == EGameID::Oblivion) ? 14 : 15; \
  260.     TestRecordAndGroupCount(Plugin, BlankEsmPath, ExpectedCount); \
  261.     TestRecordAndGroupCount(Plugin, BlankEsmPath, ExpectedCount, true); \
  262. \
  263.     /* Cleanup - Delete the temporary directory and all its contents to clean up after testing. */ \
  264.     FileManager.DeleteDirectory(*TempDir, true, true); \
  265.     return true; /* Indicate successful test completion */ \
  266. }
  267.  
  268. /* Declare automation tests for each supported Bethesda game. */
  269.  
  270. DECLARE_PLUGIN_TEST(Skyrim, Skyrim)
  271. DECLARE_PLUGIN_TEST(Oblivion, Oblivion)
  272. DECLARE_PLUGIN_TEST(Fallout3, Fallout3)
  273. DECLARE_PLUGIN_TEST(FalloutNV, FalloutNV)
  274. DECLARE_PLUGIN_TEST(Morrowind, Morrowind)
  275. DECLARE_PLUGIN_TEST(Fallout4, Fallout4)
  276.  
  277. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement