/*
* This source file is part of RmlUi, the HTML/CSS Interface Middleware
*
* For the latest information, see http://github.com/mikke89/RmlUi
*
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
* Copyright (c) 2019 The RmlUi Team, and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include "../../../Source/Core/DataModel.cpp"
#include "RmlUi/Core/Core.h"
#include "RmlUi/Core/SystemInterface.h"
#include
#include
#include
using namespace Rml;
TEST_CASE("Data variables")
{
using IntVector = Vector;
struct FunData {
int i = 99;
String x = "hello";
IntVector magic = { 3, 5, 7, 11, 13 };
};
using FunArray = Array;
struct SmartData {
bool valid = true;
FunData fun;
FunArray more_fun;
};
DataModel model;
DataTypeRegister types;
DataModelConstructor handle(&model, &types);
// Setup data type register
{
handle.RegisterArray();
if (auto fun_handle = handle.RegisterStruct())
{
fun_handle.RegisterMember("i", &FunData::i);
fun_handle.RegisterMember("x", &FunData::x);
fun_handle.RegisterMember("magic", &FunData::magic);
}
handle.RegisterArray();
if (auto smart_handle = handle.RegisterStruct())
{
smart_handle.RegisterMember("valid", &SmartData::valid);
smart_handle.RegisterMember("fun", &SmartData::fun);
smart_handle.RegisterMember("more_fun", &SmartData::more_fun);
}
}
SmartData data;
data.fun.x = "Hello, we're in SmartData!";
handle.Bind("data", &data);
// Test data addresses, setters, and assignments
{
Vector test_addresses = { "data.more_fun[1].magic[3]", "data.more_fun[1].magic.size", "data.fun.x", "data.valid" };
Vector expected_results = { ToString(data.more_fun[1].magic[3]), ToString(int(data.more_fun[1].magic.size())), ToString(data.fun.x), ToString(data.valid) };
Vector results;
for (auto& str_address : test_addresses)
{
DataAddress address = ParseAddress(str_address);
Variant result;
if (model.GetVariableInto(address, result))
results.push_back(result.Get());
}
CHECK(results == expected_results);
REQUIRE(model.GetVariable(ParseAddress("data.more_fun[1].magic[1]")).Set(Variant(String("199"))));
CHECK(data.more_fun[1].magic[1] == 199);
data.fun.magic = { 99, 190, 55, 2000, 50, 60, 70, 80, 90 };
Variant get_result;
const int magic_size = int(data.fun.magic.size());
REQUIRE(model.GetVariable(ParseAddress("data.fun.magic.size")).Get(get_result));
CHECK(get_result.Get() == ToString(magic_size));
CHECK(model.GetVariable(ParseAddress("data.fun.magic")).Size() == magic_size);
REQUIRE(model.GetVariable(ParseAddress("data.fun.magic[8]")).Get(get_result));
CHECK(get_result.Get() == "90");
}
}
struct YesStr {
int scalar;
int* scalarptr;
const int* scalarptr2;
Vector yesvector;
};
struct FooStr {
String scalar_string = "yes";
int scalar;
int* scalarptr;
YesStr* yesptr;
SharedPtr yesshr;
const int* scalarptr2;
Vector foovector;
Vector* foovectorptr;
};
struct BarStr {
int scalar;
int* scalarptr;
const int* scalarptr2;
FooStr foo;
FooStr* fooptr;
SharedPtr fooshr;
Vector barvector;
Vector* barvectorptr;
};
void registerStructs(DataModelConstructor& handle)
{
handle.RegisterArray>();
if (auto fun_handle = handle.RegisterStruct())
{
fun_handle.RegisterMember("scalar", &YesStr::scalar);
fun_handle.RegisterMember("scalarptr", &YesStr::scalarptr);
// requires constness support - fun_handle.RegisterMember("scalarptr2", &YesStr::scalarptr2);
fun_handle.RegisterMember("yesvector", &YesStr::yesvector);
}
if (auto fun_handle = handle.RegisterStruct())
{
fun_handle.RegisterMember("scalar", &FooStr::scalar);
fun_handle.RegisterMember("scalarptr", &FooStr::scalarptr);
// requires constness support - fun_handle.RegisterMember("scalarptr2", &FooStr::scalarptr2);
fun_handle.RegisterMember("string", &FooStr::scalar_string);
fun_handle.RegisterMember("yesptr", &FooStr::yesptr);
fun_handle.RegisterMember("yesshr", &FooStr::yesshr);
fun_handle.RegisterMember("foovector", &FooStr::foovector);
fun_handle.RegisterMember("foovectorptr", &FooStr::foovectorptr);
}
if (auto smart_handle = handle.RegisterStruct())
{
smart_handle.RegisterMember("scalar", &BarStr::scalar);
smart_handle.RegisterMember("scalarptr", &BarStr::scalarptr);
// requires constness support - smart_handle.RegisterMember("scalarptr2", &BarStr::scalarptr2);
smart_handle.RegisterMember("foo", &BarStr::foo);
smart_handle.RegisterMember("fooptr", &BarStr::fooptr);
smart_handle.RegisterMember("fooshr", &BarStr::fooshr);
smart_handle.RegisterMember("barvector", &BarStr::barvector);
smart_handle.RegisterMember("barvectorptr", &BarStr::barvectorptr);
}
}
TEST_CASE("Data variables pointers")
{
DataModel model;
DataTypeRegister types;
DataModelConstructor handle(&model, &types);
// Setup data type register
registerStructs(handle);
BarStr example;
handle.Bind("example", &example);
{
int for_scalars_ptrsa = 13;
int for_scalars_ptrsb = 69;
example.scalar = 666;
example.scalarptr = &for_scalars_ptrsa;
example.scalarptr2 = &for_scalars_ptrsa;
example.foo.scalar = 1337;
example.foo.scalarptr = &for_scalars_ptrsb;
example.foo.scalarptr2 = &for_scalars_ptrsb;
example.fooptr = new FooStr();
example.fooptr->scalar = 1337;
example.fooptr->scalarptr = &for_scalars_ptrsb;
example.fooptr->scalarptr2 = &for_scalars_ptrsb;
example.fooshr = std::make_shared();
*example.fooshr = example.foo;
Vector test_addresses = {"example.scalar", "example.scalarptr", /*"example.scalarptr2", */
"example.foo.scalar", "example.foo.scalarptr", /*const "example.foo.scalarptr2", */ "example.foo.string", "example.fooptr.scalar",
"example.fooptr.scalarptr", /*const "example.fooptr.scalarptr2" ,*/ "example.fooptr.string", "example.fooshr.scalar",
"example.fooshr.scalarptr", /*const "example.fooshr.scalarptr2", */ "example.fooshr.string"};
Vector expected_results = {ToString(example.scalar), ToString(*example.scalarptr), /*const ToString(*example.scalarptr2), */
ToString(example.foo.scalar), ToString(*example.foo.scalarptr),
/*const ToString(*example.foo.scalarptr2) ,*/ ToString(example.foo.scalar_string), ToString(example.fooptr->scalar),
ToString(*example.fooptr->scalarptr), /*const ToString(*example.fooptr->scalarptr2),*/ ToString(example.fooptr->scalar_string),
ToString(example.fooshr->scalar), ToString(*example.fooshr->scalarptr),
/*const ToString(*example.fooshr->scalarptr2) ,*/ ToString(example.fooshr->scalar_string)};
Vector results;
for (auto& str_address : test_addresses)
{
DataAddress address = ParseAddress(str_address);
Variant result;
if (model.GetVariableInto(address, result))
results.push_back(result.Get());
}
CHECK(results == expected_results);
}
{
delete example.fooptr;
int new_int_value = 88;
example.fooptr = new FooStr();
example.fooptr->scalar = 2137;
example.fooptr->scalarptr = &new_int_value;
example.fooptr->scalarptr2 = &new_int_value;
example.fooshr = std::make_shared();
*example.fooshr = example.foo;
Vector test_addresses = {"example.fooptr.scalar", "example.fooptr.scalarptr",
/*const "example.fooptr.scalarptr2" , */ "example.fooptr.string", "example.fooshr.scalar", "example.fooshr.scalarptr",
/*const "example.fooshr.scalarptr2" , */ "example.fooshr.string"};
Vector expected_results = {ToString(example.fooptr->scalar), ToString(*example.fooptr->scalarptr),
/*const ToString(*example.fooptr->scalarptr2),*/ ToString(example.fooptr->scalar_string), ToString(example.fooshr->scalar),
ToString(*example.fooshr->scalarptr), /*const ToString(*example.fooshr->scalarptr2),*/ ToString(example.fooshr->scalar_string)};
Vector results;
for (auto& str_address : test_addresses)
{
DataAddress address = ParseAddress(str_address);
Variant result;
if (model.GetVariableInto(address, result))
results.push_back(result.Get());
}
CHECK(results == expected_results);
}
}
TEST_CASE("Data variables pointers - nulls")
{
class SystemInterfaceImpl : public SystemInterface {
public:
double GetElapsedTime() override { return 0; }
};
SystemInterface* obj = new SystemInterfaceImpl();
SetSystemInterface(obj);
// Setup data type register
DataModel model;
DataTypeRegister types;
DataModelConstructor handle(&model, &types);
// Setup data type register
registerStructs(handle);
BarStr example;
BarStr* exampleptr;
UniquePtr exampleunq;
UniquePtr exampleunq2;
handle.Bind("example", &example);
handle.Bind("exampleptr", &exampleptr);
handle.Bind("exampleunq", &exampleunq);
handle.Bind("exampleunq2", &exampleunq2);
{
example.fooptr = nullptr;
example.fooshr = std::shared_ptr();
example.barvectorptr = nullptr;
exampleptr = nullptr;
exampleunq = MakeUnique();
exampleunq->fooptr = nullptr;
exampleunq2 = MakeUnique();
exampleunq2->fooptr = new FooStr();
exampleunq2->fooptr->scalarptr = nullptr;
exampleunq2->fooptr->yesptr = nullptr;
Vector test_addresses = {
"example.fooptr.scalarptr",
"example.fooptr",
"example.fooshr",
"example.fooshr.scalarptr",
"example.fooptr.yesptr",
"example.fooptr.yesptr.scalar",
"example.fooptr.yesshr",
"example.fooptr.yesshr.scalar",
"example.barvectorptr",
"example.barvectorptr[0]",
"example.fooptr.foovectorptr",
"example.fooptr.foovectorptr[0]",
"exampleptr",
"exampleptr.barvectorptr",
"exampleptr.barvectorptr[0]",
"exampleptr.fooptr",
"exampleptr.fooptr.foovectorptr",
"exampleptr.fooptr.foovectorptr[0]",
/*"exampleunq", - this is not null*/ "exampleunq.fooptr",
"exampleunq.fooptr.yesptr",
/*"exampleunq2", - this is not null*/ /*"exampleunq2.fooptr", - this is not null*/ "exampleunq2.fooptr.scalarptr",
"exampleunq2.fooptr.yesptr",
};
Vector expected_results = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
Vector results;
for (auto& str_address : test_addresses)
{
DataAddress address = ParseAddress(str_address);
Variant result;
model.GetVariableInto(address, result);
results.push_back(result.Get());
}
CHECK(results == expected_results);
}
}