1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-17 00:07:41 +02:00

Entities redesign and a few ERM features

* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
This commit is contained in:
AlexVinS
2018-03-17 17:58:30 +03:00
committed by AlexVinS
parent 11bb46780a
commit ecaa9f5d0b
475 changed files with 22491 additions and 7123 deletions

View File

@ -0,0 +1,39 @@
/*
* LuaSandboxTest.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "ScriptFixture.h"
namespace test
{
using ::testing::Test;
class LuaSandboxTest : public Test, public ScriptFixture
{
public:
protected:
void SetUp() override
{
ScriptFixture::setUp();
}
};
TEST_F(LuaSandboxTest, Example)
{
loadScriptFromFile("test/lua/SandboxTest.lua");
runClientServer();
}
}

View File

@ -0,0 +1,175 @@
/*
* LuaSpellEffectAPITest.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "ScriptFixture.h"
#include "../../lib/NetPacks.h"
#include "../mock/mock_ServerCallback.h"
namespace test
{
using namespace ::testing;
using namespace ::scripting;
class LuaSpellEffectAPITest : public Test, public ScriptFixture
{
public:
StrictMock<ServerCallbackMock> serverMock;
protected:
void SetUp() override
{
ScriptFixture::setUp();
}
};
TEST_F(LuaSpellEffectAPITest, ApplicableOnExpert)
{
loadScriptFromFile("test/lua/SpellEffectAPITest.lua");
context->setGlobal("effectLevel", 3);
runClientServer();
JsonNode params;
JsonNode ret = context->callGlobal("applicable", params);
JsonNode expected = JsonUtils::boolNode(true);
JsonComparer cmp(false);
cmp.compare("applicable result", ret, expected);
}
TEST_F(LuaSpellEffectAPITest, NotApplicableOnAdvanced)
{
loadScriptFromFile("test/lua/SpellEffectAPITest.lua");
context->setGlobal("effectLevel", 2);
runClientServer();
JsonNode params;
JsonNode ret = context->callGlobal("applicable", params);
JsonNode expected = JsonUtils::boolNode(false);
JsonComparer cmp(false);
cmp.compare("applicable result", ret, expected);
}
TEST_F(LuaSpellEffectAPITest, ApplicableOnLeftSideOfField)
{
loadScriptFromFile("test/lua/SpellEffectAPITest.lua");
context->setGlobal("effectLevel", 1);
runClientServer();
JsonNode params;
BattleHex hex(2,2);
JsonNode first;
first.Vector().push_back(JsonUtils::intNode(hex.hex));
first.Vector().push_back(JsonNode());
JsonNode targets;
targets.Vector().push_back(first);
params.Vector().push_back(targets);
JsonNode ret = context->callGlobal("applicableTarget", params);
JsonNode expected = JsonUtils::boolNode(true);
JsonComparer cmp(false);
cmp.compare("applicable result", ret, expected);
}
TEST_F(LuaSpellEffectAPITest, NotApplicableOnRightSideOfField)
{
loadScriptFromFile("test/lua/SpellEffectAPITest.lua");
runClientServer();
context->setGlobal("effectLevel", 1);
JsonNode params;
BattleHex hex(11,2);
JsonNode first;
first.Vector().push_back(JsonUtils::intNode(hex.hex));
first.Vector().push_back(JsonUtils::intNode(-1));
JsonNode targets;
targets.Vector().push_back(first);
params.Vector().push_back(targets);
JsonNode ret = context->callGlobal("applicableTarget", params);
JsonNode expected = JsonUtils::boolNode(false);
JsonComparer cmp(false);
cmp.compare("applicable result", ret, expected);
}
TEST_F(LuaSpellEffectAPITest, ApplyMoveUnit)
{
loadScriptFromFile("test/lua/SpellEffectAPIMoveUnit.lua");
runClientServer();
BattleHex hex1(11,2);
JsonNode unit;
unit.Vector().push_back(JsonUtils::intNode(hex1.hex));
unit.Vector().push_back(JsonUtils::intNode(42));
BattleHex hex2(5,4);
JsonNode destination;
destination.Vector().push_back(JsonUtils::intNode(hex2.hex));
destination.Vector().push_back(JsonUtils::intNode(-1));
JsonNode targets;
targets.Vector().push_back(unit);
targets.Vector().push_back(destination);
JsonNode params;
params.Vector().push_back(targets);
BattleStackMoved expected;
BattleStackMoved actual;
auto checkMove = [&](BattleStackMoved * pack)
{
EXPECT_EQ(pack->stack, 42);
EXPECT_EQ(pack->teleporting, true);
EXPECT_EQ(pack->distance, 0);
std::vector<BattleHex> toMove(1, hex2);
EXPECT_EQ(pack->tilesToMove, toMove);
};
EXPECT_CALL(serverMock, apply(Matcher<BattleStackMoved *>(_))).WillOnce(Invoke(checkMove));
context->callGlobal(&serverMock, "apply", params);
}
}

View File

@ -0,0 +1,209 @@
/*
* LuaSpellEffectTest.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "../spells/effects/EffectFixture.h"
#include "../JsonComparer.h"
#include "../mock/mock_scripting_Context.h"
#include "../mock/mock_scripting_Script.h"
#include "../mock/mock_scripting_Service.h"
#include "../mock/mock_spells_effects_Registry.h"
#include "../mock/mock_ServerCallback.h"
#include "../../../lib/VCMI_Lib.h"
#include "../../../lib/ScriptHandler.h"
#include "../../../lib/CScriptingModule.h"
namespace test
{
using namespace ::spells;
using namespace ::spells::effects;
using namespace ::scripting;
using namespace ::testing;
class LuaSpellEffectTest : public Test, public EffectFixture
{
public:
const std::string SCRIPT_NAME = "testScript";
const int32_t EFFECT_LEVEL = 1456;
const int32_t RANGE_LEVEL = 4561;
const int32_t EFFECT_POWER = 4789;
const int32_t EFFECT_DURATION = 42;
const int32_t EFFECT_VALUE = 42000;//TODO: this should be 64 bit
ServiceMock serviceMock;
ScriptMock scriptMock;
std::shared_ptr<ContextMock> contextMock;
EffectFactoryMock factoryMock;
RegistryMock registryMock;
RegistryMock::FactoryPtr factory;
StrictMock<ServerCallbackMock> serverMock;
JsonNode request;
LuaSpellEffectTest()
: EffectFixture("testScript")
{
contextMock = std::make_shared<ContextMock>();
}
void expectSettingContextVariables()
{
EXPECT_CALL(mechanicsMock, getEffectLevel()).WillRepeatedly(Return(EFFECT_LEVEL));
EXPECT_CALL(*contextMock, setGlobal(Eq("effectLevel"), Matcher<int>(Eq(EFFECT_LEVEL))));
EXPECT_CALL(mechanicsMock, getRangeLevel()).WillRepeatedly(Return(RANGE_LEVEL));
EXPECT_CALL(*contextMock, setGlobal(Eq("effectRangeLevel"), Matcher<int>(Eq(RANGE_LEVEL))));
EXPECT_CALL(mechanicsMock, getEffectPower()).WillRepeatedly(Return(EFFECT_POWER));
EXPECT_CALL(*contextMock, setGlobal(Eq("effectPower"), Matcher<int>(Eq(EFFECT_POWER))));
EXPECT_CALL(mechanicsMock, getEffectDuration()).WillRepeatedly(Return(EFFECT_DURATION));
EXPECT_CALL(*contextMock, setGlobal(Eq("effectDuration"), Matcher<int>(Eq(EFFECT_DURATION))));
EXPECT_CALL(mechanicsMock, getEffectValue()).WillRepeatedly(Return(EFFECT_VALUE));
EXPECT_CALL(*contextMock, setGlobal(Eq("effectValue"), Matcher<int>(Eq(EFFECT_VALUE))));
}
void setDefaultExpectations()
{
EXPECT_CALL(mechanicsMock, scripts()).WillRepeatedly(Return(&serviceMock));
EXPECT_CALL(*pool, getContext(Eq(&scriptMock))).WillOnce(Return(contextMock));
expectSettingContextVariables();
// JsonNode options(JsonNode::JsonType::DATA_STRUCT);
// //TODO: test passing configuration
// EffectFixture::setupEffect(options);
}
JsonNode saveRequest(const std::string &, const JsonNode & parameters)
{
JsonNode response = JsonUtils::boolNode(true);
request = parameters;
return response;
}
JsonNode saveRequest2(ServerCallback *, const std::string &, const JsonNode & parameters)
{
JsonNode response = JsonUtils::boolNode(true);
request = parameters;
return response;
}
protected:
void SetUp() override
{
EXPECT_CALL(registryMock, add(Eq(SCRIPT_NAME), _)).WillOnce(SaveArg<1>(&factory));
EXPECT_CALL(scriptMock, getName()).WillRepeatedly(ReturnRef(SCRIPT_NAME));
VLC->scriptHandler->lua->registerSpellEffect(&registryMock, &scriptMock);
GTEST_ASSERT_NE(factory, nullptr);
subject.reset(factory->create());
EffectFixture::setUp();
}
};
TEST_F(LuaSpellEffectTest, ApplicableRedirected)
{
setDefaultExpectations();
JsonNode response = JsonUtils::boolNode(true);
EXPECT_CALL(*contextMock, callGlobal(Eq("applicable"),_)).WillOnce(Return(response));//TODO: check call parameter
EXPECT_TRUE(subject->applicable(problemMock, &mechanicsMock));
}
TEST_F(LuaSpellEffectTest, ApplicableTargetRedirected)
{
setDefaultExpectations();
EXPECT_CALL(*contextMock, callGlobal(Eq("applicableTarget"),_)).WillOnce(Invoke(this, &LuaSpellEffectTest::saveRequest));
auto & unit1 = unitsFake.add(BattleSide::ATTACKER);
EffectTarget target;
BattleHex hex1(6,7);
BattleHex hex2(7,8);
int32_t id1 = 42;
EXPECT_CALL(unit1, unitId()).WillOnce(Return(id1));
target.emplace_back(&unit1, hex1);
target.emplace_back(hex2);
EXPECT_TRUE(subject->applicable(problemMock, &mechanicsMock, target));
JsonNode first;
first.Vector().push_back(JsonUtils::intNode(hex1.hex));
first.Vector().push_back(JsonUtils::intNode(id1));
JsonNode second;
second.Vector().push_back(JsonUtils::intNode(hex2.hex));
second.Vector().push_back(JsonUtils::intNode(-1));
JsonNode targets;
targets.Vector().push_back(first);
targets.Vector().push_back(second);
JsonNode expected;
expected.Vector().push_back(targets);
JsonComparer c(false);
c.compare("applicableTarget request", request, expected);
}
TEST_F(LuaSpellEffectTest, ApplyRedirected)
{
setDefaultExpectations();
EXPECT_CALL(*contextMock, callGlobal(Eq(&serverMock), Eq("apply"),_)).WillOnce(Invoke(this, &LuaSpellEffectTest::saveRequest2));
auto & unit1 = unitsFake.add(BattleSide::ATTACKER);
EffectTarget target;
BattleHex hex1(6,7);
int32_t id1 = 42;
EXPECT_CALL(unit1, unitId()).WillOnce(Return(id1));
target.emplace_back(&unit1, hex1);
subject->apply(&serverMock, &mechanicsMock, target);
JsonNode first;
first.Vector().push_back(JsonUtils::intNode(hex1.hex));
first.Vector().push_back(JsonUtils::intNode(id1));
JsonNode targets;
targets.Vector().push_back(first);
JsonNode expected;
expected.Vector().push_back(targets);
JsonComparer c(false);
c.compare("apply request", request, expected);
}
}

116
test/scripting/PoolTest.cpp Normal file
View File

@ -0,0 +1,116 @@
/*
* PoolTest.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "../../lib/ScriptHandler.h"
#include "../mock/mock_Environment.h"
#include "../mock/mock_ServerCallback.h"
#include "../mock/mock_scripting_Context.h"
#include "../mock/mock_scripting_Script.h"
#include "../JsonComparer.h"
namespace scripting
{
namespace test
{
using namespace ::testing;
class PoolTest : public Test
{
public:
const std::string SCRIPT_NAME = "TEST SCRIPT";
NiceMock<EnvironmentMock> env;
StrictMock<ScriptMock> script;
std::shared_ptr<PoolImpl> subject;
StrictMock<ServerCallbackMock> server;
void setDefaultExpectations()
{
EXPECT_CALL(script, getName()).WillRepeatedly(ReturnRef(SCRIPT_NAME));
}
protected:
void SetUp() override
{
subject = std::make_shared<PoolImpl>(&env);
}
};
TEST_F(PoolTest, CreatesContext)
{
setDefaultExpectations();
auto context = std::make_shared<NiceMock<ContextMock>>();
EXPECT_CALL(script, createContext(Eq(&env))).WillOnce(Return(context));
EXPECT_EQ(context, subject->getContext(&script));
}
TEST_F(PoolTest, ReturnsCachedContext)
{
setDefaultExpectations();
auto context = std::make_shared<NiceMock<ContextMock>>();
EXPECT_CALL(script, createContext(Eq(&env))).WillOnce(Return(context));
auto first = subject->getContext(&script);
EXPECT_EQ(first, subject->getContext(&script));
}
TEST_F(PoolTest, CreatesNewContextWithEmptyState)
{
setDefaultExpectations();
auto context = std::make_shared<StrictMock<ContextMock>>();
EXPECT_CALL(script, createContext(Eq(&env))).WillOnce(Return(context));
EXPECT_CALL(*context, run(Eq(JsonNode()))).Times(1);
subject->getContext(&script);//return value ignored
}
TEST_F(PoolTest, SavesScriptState)
{
setDefaultExpectations();
auto context = std::make_shared<StrictMock<ContextMock>>();
EXPECT_CALL(script, createContext(Eq(&env))).WillOnce(Return(context));
EXPECT_CALL(*context, run(Eq(JsonNode()))).Times(1);
subject->getContext(&script);
JsonNode expectedState;
expectedState[SCRIPT_NAME]["foo"].String() = "bar";
EXPECT_CALL(*context, saveState()).WillOnce(Return(expectedState[SCRIPT_NAME]));
JsonNode actualState;
subject->serializeState(true, actualState);
JsonComparer c(false);
c.compare("state", actualState, expectedState);
}
TEST_F(PoolTest, LoadsScriptState)
{
setDefaultExpectations();
auto context = std::make_shared<StrictMock<ContextMock>>();
EXPECT_CALL(script, createContext(Eq(&env))).WillOnce(Return(context));
JsonNode expectedState;
expectedState[SCRIPT_NAME]["foo"].String() = "bar";
subject->serializeState(false, expectedState);
EXPECT_CALL(*context, run(Eq(expectedState[SCRIPT_NAME]))).Times(1);
subject->getContext(&script);
}
}
}

View File

@ -0,0 +1,92 @@
/*
* ScriptFixture.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "ScriptFixture.h"
namespace test
{
using namespace ::testing;
using namespace ::scripting;
ScriptFixture::ScriptFixture() = default;
ScriptFixture::~ScriptFixture() = default;
void ScriptFixture::loadScriptFromFile(const std::string & path)
{
JsonNode scriptConfig(JsonNode::JsonType::DATA_STRUCT);
scriptConfig["source"].String() = path;
loadScript(scriptConfig);
}
void ScriptFixture::loadScript(const JsonNode & scriptConfig)
{
subject = VLC->scriptHandler->loadFromJson(&loggerMock, "core", scriptConfig, "test");
GTEST_ASSERT_NE(subject, nullptr);
context = subject->createContext(&environmentMock);
EXPECT_CALL(*pool, getContext(Eq(subject.get()))).WillRepeatedly(Return(context));
}
void ScriptFixture::loadScript(ModulePtr module, const std::string & scriptSource)
{
subject = std::make_shared<ScriptImpl>(VLC->scriptHandler);
subject->host = module;
subject->sourceText = scriptSource;
subject->identifier = "test";
subject->compile(&loggerMock);
context = subject->createContext(&environmentMock);
EXPECT_CALL(*pool, getContext(Eq(subject.get()))).WillRepeatedly(Return(context));
}
void ScriptFixture::loadScript(ModulePtr module, const std::vector<std::string> & scriptSource)
{
std::string source = boost::algorithm::join(scriptSource, "\n");
loadScript(module, source);
}
void ScriptFixture::setUp()
{
pool = std::make_shared<PoolMock>();
EXPECT_CALL(environmentMock, battle()).WillRepeatedly(Return(&binfoMock));
EXPECT_CALL(environmentMock, game()).WillRepeatedly(Return(&infoMock));
EXPECT_CALL(environmentMock, logger()).WillRepeatedly(Return(&loggerMock));
EXPECT_CALL(environmentMock, eventBus()).WillRepeatedly(Return(&eventBus));
EXPECT_CALL(environmentMock, services()).WillRepeatedly(Return(&servicesMock));
}
JsonNode ScriptFixture::runClientServer(const JsonNode & scriptState)
{
context->run(scriptState);
return context->saveState();
}
JsonNode ScriptFixture::runServer(const JsonNode & scriptState)
{
context->run(&serverMock, scriptState);
return context->saveState();
}
JsonNode ScriptFixture::runScript(ModulePtr module, const std::string & scriptSource, const JsonNode & scriptState)
{
loadScript(module, scriptSource);
return runClientServer(scriptState);
}
}

View File

@ -0,0 +1,82 @@
/*
* ScriptFixture.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include <vstd/RNG.h>
#include <vcmi/events/EventBus.h>
#include "../../lib/JsonNode.h"
#include "../../lib/HeroBonus.h"
#include "../../lib/ScriptHandler.h"
#include "../../lib/NetPacksBase.h"
#include "../../lib/battle/CBattleInfoCallback.h"
#include "../mock/mock_ServerCallback.h"
#include "../mock/mock_IBattleInfoCallback.h"
#include "../mock/mock_IGameInfoCallback.h"
#include "../mock/mock_battle_IBattleState.h"
#include "../mock/mock_scripting_Pool.h"
#include "../mock/mock_Environment.h"
#include "../mock/mock_Services.h"
#include "../mock/mock_vstd_CLoggerBase.h"
#include "../mock/BattleFake.h"
#include "../JsonComparer.h"
namespace test
{
using namespace ::testing;
using namespace ::scripting;
using ::events::EventBus;
class ScriptFixture
{
public:
EventBus eventBus;
std::shared_ptr<PoolMock> pool;
std::shared_ptr<ScriptImpl> subject;
std::shared_ptr<Context> context;
battle::UnitsFake unitsFake;
StrictMock<EnvironmentMock> environmentMock;
StrictMock<IBattleInfoCallbackMock> binfoMock;
StrictMock<IGameInfoCallbackMock> infoMock;
StrictMock<ServerCallbackMock> serverMock;
StrictMock<ServicesMock> servicesMock;
LoggerMock loggerMock;
ScriptFixture();
virtual ~ScriptFixture();
void loadScriptFromFile(const std::string & path);
void loadScript(const JsonNode & scriptConfig);
void loadScript(ModulePtr module, const std::string & scriptSource);
void loadScript(ModulePtr module, const std::vector<std::string> & scriptSource);
JsonNode runClientServer(const JsonNode & scriptState = JsonNode());
JsonNode runServer(const JsonNode & scriptState = JsonNode());
JsonNode runScript(ModulePtr module, const std::string & scriptSource, const JsonNode & scriptState = JsonNode());
protected:
void setUp();
private:
};
}