mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 08:26:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			243 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			243 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "gmock/gmock.h"
 | |
| #include <gtest/gtest.h>
 | |
| 
 | |
| #include <fstream>
 | |
| 
 | |
| #include <components/esm3/esmreader.hpp>
 | |
| #include <components/esm3/esmwriter.hpp>
 | |
| #include <components/esm3/readerscache.hpp>
 | |
| #include <components/lua/configuration.hpp>
 | |
| #include <components/lua/serialization.hpp>
 | |
| 
 | |
| #include "../testing_util.hpp"
 | |
| 
 | |
| namespace
 | |
| {
 | |
| 
 | |
|     using testing::ElementsAre;
 | |
|     using testing::Pair;
 | |
| 
 | |
|     std::vector<std::pair<int, std::string>> asVector(const LuaUtil::ScriptIdsWithInitializationData& d)
 | |
|     {
 | |
|         std::vector<std::pair<int, std::string>> res;
 | |
|         for (const auto& [k, v] : d)
 | |
|             res.emplace_back(k, std::string(v));
 | |
|         return res;
 | |
|     }
 | |
| 
 | |
|     TEST(LuaConfigurationTest, ValidOMWScripts)
 | |
|     {
 | |
|         ESM::LuaScriptsCfg cfg;
 | |
|         LuaUtil::parseOMWScripts(cfg, R"X(
 | |
|             # Lines starting with '#' are comments
 | |
|             GLOBAL:  my_mod/#some_global_script.lua
 | |
| 
 | |
|             # Script that will be automatically attached to the player
 | |
|             PLAYER :my_mod/player.lua
 | |
|             CUSTOM : my_mod/some_other_script.lua
 | |
|             NPC , CREATURE PLAYER : my_mod/some_other_script.lua)X");
 | |
|         LuaUtil::parseOMWScripts(cfg, ":my_mod/player.LUA  \r\nCREATURE,CUSTOM: my_mod/creature.lua\r\n");
 | |
| 
 | |
|         ASSERT_EQ(cfg.mScripts.size(), 6);
 | |
|         EXPECT_EQ(LuaUtil::scriptCfgToString(cfg.mScripts[0]), "GLOBAL : my_mod/#some_global_script.lua");
 | |
|         EXPECT_EQ(LuaUtil::scriptCfgToString(cfg.mScripts[1]), "PLAYER : my_mod/player.lua");
 | |
|         EXPECT_EQ(LuaUtil::scriptCfgToString(cfg.mScripts[2]), "CUSTOM : my_mod/some_other_script.lua");
 | |
|         EXPECT_EQ(LuaUtil::scriptCfgToString(cfg.mScripts[3]), "PLAYER NPC CREATURE : my_mod/some_other_script.lua");
 | |
|         EXPECT_EQ(LuaUtil::scriptCfgToString(cfg.mScripts[4]), ": my_mod/player.LUA");
 | |
|         EXPECT_EQ(LuaUtil::scriptCfgToString(cfg.mScripts[5]), "CUSTOM CREATURE : my_mod/creature.lua");
 | |
| 
 | |
|         LuaUtil::ScriptsConfiguration conf;
 | |
|         conf.init(std::move(cfg));
 | |
|         ASSERT_EQ(conf.size(), 4);
 | |
|         EXPECT_EQ(LuaUtil::scriptCfgToString(conf[0]), "GLOBAL : my_mod/#some_global_script.lua");
 | |
|         // cfg.mScripts[1] is overridden by cfg.mScripts[4]
 | |
|         // cfg.mScripts[2] is overridden by cfg.mScripts[3]
 | |
|         EXPECT_EQ(LuaUtil::scriptCfgToString(conf[1]), "PLAYER NPC CREATURE : my_mod/some_other_script.lua");
 | |
|         EXPECT_EQ(LuaUtil::scriptCfgToString(conf[2]), ": my_mod/player.LUA");
 | |
|         EXPECT_EQ(LuaUtil::scriptCfgToString(conf[3]), "CUSTOM CREATURE : my_mod/creature.lua");
 | |
| 
 | |
|         EXPECT_THAT(asVector(conf.getGlobalConf()), ElementsAre(Pair(0, "")));
 | |
|         EXPECT_THAT(asVector(conf.getPlayerConf()), ElementsAre(Pair(1, "")));
 | |
|         EXPECT_THAT(asVector(conf.getLocalConf(ESM::REC_CONT, "something", ESM::RefNum())), ElementsAre());
 | |
|         EXPECT_THAT(asVector(conf.getLocalConf(ESM::REC_NPC_, "something", ESM::RefNum())), ElementsAre(Pair(1, "")));
 | |
|         EXPECT_THAT(asVector(conf.getLocalConf(ESM::REC_CREA, "something", ESM::RefNum())), ElementsAre(Pair(1, ""), Pair(3, "")));
 | |
| 
 | |
|         // Check that initialization cleans old data
 | |
|         cfg = ESM::LuaScriptsCfg();
 | |
|         conf.init(std::move(cfg));
 | |
|         EXPECT_EQ(conf.size(), 0);
 | |
|     }
 | |
| 
 | |
|     TEST(LuaConfigurationTest, InvalidOMWScripts)
 | |
|     {
 | |
|         ESM::LuaScriptsCfg cfg;
 | |
|         EXPECT_ERROR(LuaUtil::parseOMWScripts(cfg, "GLOBAL: something"),
 | |
|                      "Lua script should have suffix '.lua', got: GLOBAL: something");
 | |
|         EXPECT_ERROR(LuaUtil::parseOMWScripts(cfg, "something.lua"),
 | |
|                      "No flags found in: something.lua");
 | |
| 
 | |
|         cfg.mScripts.clear();
 | |
|         EXPECT_NO_THROW(LuaUtil::parseOMWScripts(cfg, "GLOBAL, PLAYER: something.lua"));
 | |
|         LuaUtil::ScriptsConfiguration conf;
 | |
|         EXPECT_ERROR(conf.init(std::move(cfg)), "Global script can not have local flags");
 | |
|     }
 | |
| 
 | |
|     TEST(LuaConfigurationTest, ConfInit)
 | |
|     {
 | |
|         ESM::LuaScriptsCfg cfg;
 | |
|         ESM::LuaScriptCfg& script1 = cfg.mScripts.emplace_back();
 | |
|         script1.mScriptPath = "Script1.lua";
 | |
|         script1.mInitializationData = "data1";
 | |
|         script1.mFlags = ESM::LuaScriptCfg::sPlayer;
 | |
|         script1.mTypes.push_back(ESM::REC_CREA);
 | |
|         script1.mRecords.push_back({true, "record1", "dataRecord1"});
 | |
|         script1.mRefs.push_back({true, 2, 3, ""});
 | |
|         script1.mRefs.push_back({true, 2, 4, ""});
 | |
| 
 | |
|         ESM::LuaScriptCfg& script2 = cfg.mScripts.emplace_back();
 | |
|         script2.mScriptPath = "Script2.lua";
 | |
|         script2.mFlags = ESM::LuaScriptCfg::sCustom;
 | |
|         script2.mTypes.push_back(ESM::REC_CONT);
 | |
| 
 | |
|         ESM::LuaScriptCfg& script1Extra = cfg.mScripts.emplace_back();
 | |
|         script1Extra.mScriptPath = "script1.LUA";
 | |
|         script1Extra.mFlags = ESM::LuaScriptCfg::sCustom | ESM::LuaScriptCfg::sMerge;
 | |
|         script1Extra.mTypes.push_back(ESM::REC_NPC_);
 | |
|         script1Extra.mRecords.push_back({false, "rat", ""});
 | |
|         script1Extra.mRecords.push_back({true, "record2", ""});
 | |
|         script1Extra.mRefs.push_back({true, 3, 5, "dataRef35"});
 | |
|         script1Extra.mRefs.push_back({false, 2, 3, ""});
 | |
| 
 | |
|         LuaUtil::ScriptsConfiguration conf;
 | |
|         conf.init(cfg);
 | |
|         ASSERT_EQ(conf.size(), 2);
 | |
|         EXPECT_EQ(LuaUtil::scriptCfgToString(conf[0]),
 | |
|                   "CUSTOM PLAYER CREATURE NPC : Script1.lua ; data 5 bytes ; 3 records ; 4 objects");
 | |
|         EXPECT_EQ(LuaUtil::scriptCfgToString(conf[1]), "CUSTOM CONTAINER : Script2.lua");
 | |
| 
 | |
|         EXPECT_THAT(asVector(conf.getPlayerConf()), ElementsAre(Pair(0, "data1")));
 | |
|         EXPECT_THAT(asVector(conf.getLocalConf(ESM::REC_CONT, "something", ESM::RefNum())), ElementsAre(Pair(1, "")));
 | |
|         EXPECT_THAT(asVector(conf.getLocalConf(ESM::REC_CREA, "guar", ESM::RefNum())), ElementsAre(Pair(0, "data1")));
 | |
|         EXPECT_THAT(asVector(conf.getLocalConf(ESM::REC_CREA, "rat", ESM::RefNum())), ElementsAre());
 | |
|         EXPECT_THAT(asVector(conf.getLocalConf(ESM::REC_DOOR, "record1", ESM::RefNum())), ElementsAre(Pair(0, "dataRecord1")));
 | |
|         EXPECT_THAT(asVector(conf.getLocalConf(ESM::REC_DOOR, "record2", ESM::RefNum())), ElementsAre(Pair(0, "data1")));
 | |
|         EXPECT_THAT(asVector(conf.getLocalConf(ESM::REC_NPC_, "record3", {1, 1})), ElementsAre(Pair(0, "data1")));
 | |
|         EXPECT_THAT(asVector(conf.getLocalConf(ESM::REC_NPC_, "record3", {2, 3})), ElementsAre());
 | |
|         EXPECT_THAT(asVector(conf.getLocalConf(ESM::REC_NPC_, "record3", {3, 5})), ElementsAre(Pair(0, "dataRef35")));
 | |
|         EXPECT_THAT(asVector(conf.getLocalConf(ESM::REC_CONT, "record4", {2, 4})), ElementsAre(Pair(0, "data1"), Pair(1, "")));
 | |
| 
 | |
|         ESM::LuaScriptCfg& script3 = cfg.mScripts.emplace_back();
 | |
|         script3.mScriptPath = "script1.lua";
 | |
|         script3.mFlags = ESM::LuaScriptCfg::sGlobal;
 | |
|         EXPECT_ERROR(conf.init(cfg), "Flags mismatch for script1.lua");
 | |
|     }
 | |
| 
 | |
|     TEST(LuaConfigurationTest, Serialization)
 | |
|     {
 | |
|         sol::state lua;
 | |
|         LuaUtil::BasicSerializer serializer;
 | |
| 
 | |
|         ESM::ESMWriter writer;
 | |
|         writer.setAuthor("");
 | |
|         writer.setDescription("");
 | |
|         writer.setRecordCount(1);
 | |
|         writer.setFormat(ESM::Header::CurrentFormat);
 | |
|         writer.setVersion();
 | |
|         writer.addMaster("morrowind.esm", 0);
 | |
| 
 | |
|         ESM::LuaScriptsCfg cfg;
 | |
|         std::string luaData;
 | |
|         {
 | |
|             sol::table data(lua, sol::create);
 | |
|             data["number"] = 5;
 | |
|             data["string"] = "some value";
 | |
|             data["fargoth"] = ESM::RefNum{128964, 1};
 | |
|             luaData = LuaUtil::serialize(data, &serializer);
 | |
|         }
 | |
|         {
 | |
|             ESM::LuaScriptCfg& script = cfg.mScripts.emplace_back();
 | |
|             script.mScriptPath = "test_global.lua";
 | |
|             script.mFlags = ESM::LuaScriptCfg::sGlobal;
 | |
|             script.mInitializationData = luaData;
 | |
|         }
 | |
|         {
 | |
|             ESM::LuaScriptCfg& script = cfg.mScripts.emplace_back();
 | |
|             script.mScriptPath = "test_local.lua";
 | |
|             script.mFlags = ESM::LuaScriptCfg::sMerge;
 | |
|             script.mTypes.push_back(ESM::REC_DOOR);
 | |
|             script.mTypes.push_back(ESM::REC_MISC);
 | |
|             script.mRecords.push_back({true, "rat", luaData});
 | |
|             script.mRecords.push_back({false, "chargendoorjournal", ""});
 | |
|             script.mRefs.push_back({true, 128964, 1, ""});
 | |
|             script.mRefs.push_back({true, 128962, 1, luaData});
 | |
|         }
 | |
| 
 | |
|         std::stringstream stream;
 | |
|         writer.save(stream);
 | |
|         writer.startRecord(ESM::REC_LUAL);
 | |
|         cfg.save(writer);
 | |
|         writer.endRecord(ESM::REC_LUAL);
 | |
|         writer.close();
 | |
|         std::string serializedOMWAddon = stream.str();
 | |
| 
 | |
|         {
 | |
|             // Save for manual testing.
 | |
|             std::ofstream f(TestingOpenMW::outputFilePath("lua_conf_test.omwaddon"), std::ios::binary);
 | |
|             f << serializedOMWAddon;
 | |
|             f.close();
 | |
|         }
 | |
| 
 | |
|         ESM::ESMReader reader;
 | |
|         reader.open(std::make_unique<std::istringstream>(serializedOMWAddon), "lua_conf_test.omwaddon");
 | |
|         ASSERT_EQ(reader.getRecordCount(), 1);
 | |
|         ASSERT_EQ(reader.getRecName().toInt(), ESM::REC_LUAL);
 | |
|         reader.getRecHeader();
 | |
|         ESM::LuaScriptsCfg loadedCfg;
 | |
|         loadedCfg.load(reader);
 | |
| 
 | |
|         ASSERT_EQ(loadedCfg.mScripts.size(), cfg.mScripts.size());
 | |
|         for (size_t i = 0; i < cfg.mScripts.size(); ++i)
 | |
|         {
 | |
|             EXPECT_EQ(loadedCfg.mScripts[i].mScriptPath, cfg.mScripts[i].mScriptPath);
 | |
|             EXPECT_EQ(loadedCfg.mScripts[i].mFlags, cfg.mScripts[i].mFlags);
 | |
|             EXPECT_EQ(loadedCfg.mScripts[i].mInitializationData, cfg.mScripts[i].mInitializationData);
 | |
|             ASSERT_EQ(loadedCfg.mScripts[i].mTypes.size(), cfg.mScripts[i].mTypes.size());
 | |
|             for (size_t j = 0; j < cfg.mScripts[i].mTypes.size(); ++j)
 | |
|                 EXPECT_EQ(loadedCfg.mScripts[i].mTypes[j], cfg.mScripts[i].mTypes[j]);
 | |
|             ASSERT_EQ(loadedCfg.mScripts[i].mRecords.size(), cfg.mScripts[i].mRecords.size());
 | |
|             for (size_t j = 0; j < cfg.mScripts[i].mRecords.size(); ++j)
 | |
|             {
 | |
|                 EXPECT_EQ(loadedCfg.mScripts[i].mRecords[j].mAttach, cfg.mScripts[i].mRecords[j].mAttach);
 | |
|                 EXPECT_EQ(loadedCfg.mScripts[i].mRecords[j].mRecordId, cfg.mScripts[i].mRecords[j].mRecordId);
 | |
|                 EXPECT_EQ(loadedCfg.mScripts[i].mRecords[j].mInitializationData, cfg.mScripts[i].mRecords[j].mInitializationData);
 | |
|             }
 | |
|             ASSERT_EQ(loadedCfg.mScripts[i].mRefs.size(), cfg.mScripts[i].mRefs.size());
 | |
|             for (size_t j = 0; j < cfg.mScripts[i].mRefs.size(); ++j)
 | |
|             {
 | |
|                 EXPECT_EQ(loadedCfg.mScripts[i].mRefs[j].mAttach, cfg.mScripts[i].mRefs[j].mAttach);
 | |
|                 EXPECT_EQ(loadedCfg.mScripts[i].mRefs[j].mRefnumIndex, cfg.mScripts[i].mRefs[j].mRefnumIndex);
 | |
|                 EXPECT_EQ(loadedCfg.mScripts[i].mRefs[j].mRefnumContentFile, cfg.mScripts[i].mRefs[j].mRefnumContentFile);
 | |
|                 EXPECT_EQ(loadedCfg.mScripts[i].mRefs[j].mInitializationData, cfg.mScripts[i].mRefs[j].mInitializationData);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         {
 | |
|             ESM::ReadersCache readers(4);
 | |
|             readers.get(0)->openRaw(std::make_unique<std::istringstream>("dummyData"), "a.omwaddon");
 | |
|             readers.get(1)->openRaw(std::make_unique<std::istringstream>("dummyData"), "b.omwaddon");
 | |
|             readers.get(2)->openRaw(std::make_unique<std::istringstream>("dummyData"), "Morrowind.esm");
 | |
|             readers.get(3)->openRaw(std::make_unique<std::istringstream>("dummyData"), "c.omwaddon");
 | |
|             reader.setIndex(3);
 | |
|             reader.resolveParentFileIndices(readers);
 | |
|         }
 | |
|         loadedCfg.adjustRefNums(reader);
 | |
|         EXPECT_EQ(loadedCfg.mScripts[1].mRefs[0].mRefnumIndex, cfg.mScripts[1].mRefs[0].mRefnumIndex);
 | |
|         EXPECT_EQ(loadedCfg.mScripts[1].mRefs[0].mRefnumContentFile, 2);
 | |
|         {
 | |
|             sol::table data = LuaUtil::deserialize(lua.lua_state(), loadedCfg.mScripts[1].mRefs[1].mInitializationData, &serializer);
 | |
|             ESM::RefNum adjustedRef = data["fargoth"].get<ESM::RefNum>();
 | |
|             EXPECT_EQ(adjustedRef.mIndex, 128964u);
 | |
|             EXPECT_EQ(adjustedRef.mContentFile, 2);
 | |
|         }
 | |
|     }
 | |
| }
 |