Files
2024-11-04 00:18:31 +01:00

211 lines
5.7 KiB
C++

#include <lcf/data.h>
#include "doctest.h"
#include "game_dynrpg.h"
#include "game_variables.h"
#include "test_mock_actor.h"
TEST_SUITE_BEGIN("DynRPG");
TEST_CASE("Basic parsing") {
std::vector<std::string> args;
CHECK(DynRpg::ParseCommand(R"(@FunC abc , "Hello World", 42, 1.5, 1.5a, "Str"",ing")", args) == "func");
CHECK(args.size() == 6);
CHECK(args[0] == "abc");
CHECK(args[1] == "Hello World");
CHECK(args[2] == "42");
CHECK(args[3] == "1.5");
CHECK(args[4] == "1.5a"); // DynRPG returns 1.5, not critical, we extract float later
CHECK(args[5] == "Str\",ing");
}
TEST_CASE("Unterminated literal") {
std::vector<std::string> args;
CHECK(DynRpg::ParseCommand("@FunC abc , \"Hello ", args) == "func");
CHECK(args.size() == 2);
CHECK(args[0] == "abc");
CHECK(args[1] == "Hello ");
}
TEST_CASE("Comma") {
std::vector<std::string> args;
CHECK(DynRpg::ParseCommand("@FunC , abc,,,xyz,,", args) == "func");
CHECK(args.size() == 7);
CHECK(args[0] == "");
CHECK(args[1] == "abc");
CHECK(args[2] == "");
CHECK(args[3] == "");
CHECK(args[4] == "xyz");
CHECK(args[5] == "");
CHECK(args[6] == "");
}
TEST_CASE("Tokens") {
std::vector<std::string> args;
CHECK(DynRpg::ParseCommand("@FunC ab cd,Bla , Bla Blub, Ab\"C \"d e", args) == "func");
CHECK(args.size() == 4);
CHECK(args[0] == "abcd");
CHECK(args[1] == "bla");
CHECK(args[2] == "blablub");
CHECK(args[3] == "ab\"c\"de");
}
TEST_CASE("Variable names") {
const MockActor m;
std::vector<int32_t> vars = {100, 4, 2, 1};
Main_Data::game_variables->SetData(vars);
Main_Data::game_variables->SetWarning(0);
std::vector<std::string> args;
CHECK(DynRpg::ParseCommand("@FunC V2,VV3,VVVV3", args) == "func");
CHECK(args.size() == 3);
CHECK(args[0] == "4");
CHECK(args[1] == "4");
CHECK(args[2] == "100");
}
TEST_CASE("Variable names with junk") {
const MockActor m;
std::vector<int32_t> vars = {0, 4, 2, 1};
Main_Data::game_variables->SetData(vars);
Main_Data::game_variables->SetWarning(0);
std::vector<std::string> args;
CHECK(DynRpg::ParseCommand("@FunC V2junk,VV3 junk,VVVjunk3", args) == "func");
CHECK(args.size() == 3);
CHECK(args[0] == "4");
CHECK(args[1] == "4");
CHECK(args[2] == "vvvjunk3");
}
TEST_CASE("Variable names with junk") {
const MockActor m;
std::vector<int32_t> vars = {0, 4, 2};
Main_Data::game_variables->SetData(vars);
Main_Data::game_variables->SetWarning(0);
MakeDBActor(2);
MakeDBActor(4);
Main_Data::game_actors->GetActor(2)->SetName("Actor 2");
Main_Data::game_actors->GetActor(4)->SetName("Actor 4");
std::vector<std::string> args;
CHECK(DynRpg::ParseCommand("@FunC N2junk,NV3 junk,NVV3", args) == "func");
CHECK(args.size() == 3);
CHECK(args[0] == "Actor 2");
CHECK(args[1] == "Actor 2");
CHECK(args[2] == "Actor 4");
}
TEST_CASE("Arg parse") {
const MockActor m; // disable log
// 2.5x not tested here because the result differs between different C++ libraries
// See: https://bugs.llvm.org/show_bug.cgi?id=17782
std::vector<std::string> args = {"A", "1y", "2.5 x"};
bool okay = false;
std::string s, s2;
int i, i2;
float f;
std::tie(s, i, f) = DynRpg::ParseArgs<std::string, int, float>("func", args, &okay);
CHECK(okay);
CHECK(s == "A");
CHECK(i == 1);
CHECK(f == 2.5);
std::tie(i, i2, f) = DynRpg::ParseArgs<int, int, float>("func", args, &okay);
CHECK(!okay);
CHECK(i == 0);
CHECK(i2 == 0);
CHECK(f == 0.0);
std::tie(f, i,s) = DynRpg::ParseArgs<float, int, std::string>("func", args, &okay);
CHECK(!okay);
CHECK(f == 0.0f);
CHECK(i == 0);
CHECK(s == "");
std::tie(s, s2, i) = DynRpg::ParseArgs<std::string, std::string, int>("func", args, &okay);
CHECK(okay);
CHECK(s == "A");
CHECK(s2 == "1y");
CHECK(i == 2);
std::tie(std::ignore, std::ignore, s) = DynRpg::ParseArgs<std::string, std::string, std::string>("func", args, &okay);
CHECK(okay);
CHECK(s == "2.5 x");
// DynRPG reports string here but for convenience empty strings are 0
args = {"", ""};
std::tie(f, i) = DynRpg::ParseArgs<float, int>("func", args, &okay);
CHECK(okay);
CHECK(f == 0.0f);
CHECK(i == 0);
}
TEST_CASE("easyrpg dynrpg invoke") {
Game_DynRpg dyn;
const MockActor m;
Player::game_config.patch_dynrpg.Set(true);
std::vector<int32_t> vars = {-1};
Main_Data::game_variables->SetData(vars);
Main_Data::game_variables->SetWarning(0);
dyn.Invoke("@easyrpg_add 1, 2, 4");
CHECK(Main_Data::game_variables->Get(1) == 6);
// Invalid, stays 6
dyn.Invoke("@easyrpg_add 1, 2, a");
CHECK(Main_Data::game_variables->Get(1) == 6);
// Not enough args, stays 6
dyn.Invoke("@easyrpg_add 1");
CHECK(Main_Data::game_variables->Get(1) == 6);
dyn.Invoke("@easyrpg_add 1, 2");
CHECK(Main_Data::game_variables->Get(1) == 2);
dyn.Invoke("@call easyrpg_add, 1, 4.3, 7.7");
CHECK(Main_Data::game_variables->Get(1) == 11);
// Extra args ignored
dyn.Invoke("@call easyrpg_add, 1, 4, 8, -14.0");
CHECK(Main_Data::game_variables->Get(1) == -2);
// Invalid func, stays -2
dyn.Invoke("@call easyrpg_xxx, 1, 4, 7");
CHECK(Main_Data::game_variables->Get(1) == -2);
// does not crash
dyn.Invoke("@unknownfunc 1, 2, 3");
}
TEST_CASE("Incompatible changes") {
const MockActor m; // disable log
std::vector<std::string> args;
// Empty function name (DynRPG accepts this, but makes no sense)
CHECK(DynRpg::ParseCommand("@ aaa", args).empty());
// Text after literal (DynRPG accepts this, extra data is removed)
CHECK(DynRpg::ParseCommand(R"(@FunC "Arg"junk)", args).empty());
// DynRpg interprets as float and reads V3
Main_Data::game_variables = std::make_unique<Game_Variables>(-99999, 99999);
std::vector<int32_t> vars = {0, 4, 5};
Main_Data::game_variables->SetData(vars);
args.clear();
CHECK(DynRpg::ParseCommand("@FunC V2.5", args) == "func");
CHECK(args.size() == 1);
CHECK(args[0] == "4");
}
TEST_SUITE_END();