1
0
Fork 0

Nasal unit-testing: allow deep struct equality.

Add a deep comparison of vecs and hashes, when deciding equality in
test assertions.
This commit is contained in:
James Turner 2020-07-08 15:19:47 +01:00
parent 2bb15866b8
commit f0d3663102
6 changed files with 139 additions and 13 deletions

View file

@ -1956,3 +1956,54 @@ naRef NasalXMLVisitor::make_string(const char* s, int n)
return naStr_fromdata(naNewString(_c), const_cast<char *>(s),
n < 0 ? strlen(s) : n);
}
// like naEqual, but checks vector/hash recursively
// note this will not tolerate a recursively defined Nasal structure
// (such as globals.)
int nasalStructEqual(naContext ctx, naRef a, naRef b)
{
if (naIsVector(a) && naIsVector(b)) {
const int aSz = naVec_size(a),
bSz = naVec_size(b);
if (aSz != bSz)
return 0;
for (int i = 0; i < aSz; ++i) {
int eq = nasalStructEqual(ctx, naVec_get(a, i), naVec_get(b, i));
if (!eq)
return 0;
}
// all elements equal, we're done
return 1;
}
if (naIsHash(a) && naIsHash(b)) {
naRef keysVec = naNewVector(ctx);
naHash_keys(keysVec, a);
const auto aSz = naVec_size(keysVec);
// first check key count, that's fast
if (aSz != naHash_size(b))
return 0;
for (int i = 0; i < aSz; i++) {
naRef key = naVec_get(keysVec, i);
naRef aValue, bValue;
if (!naHash_get(a, key, &aValue) || !naHash_get(b, key, &bValue)) {
return 0;
}
int eq = nasalStructEqual(ctx, aValue, bValue);
if (!eq) {
return 0;
}
}
// all values matched, we're good
return 1;
}
return naEqual(a, b);
}

View file

@ -20,6 +20,12 @@
#include <simgear/nasal/nasal.h>
#include <simgear/xml/easyxml.hxx>
/**
@breif wrapper for naEqual which recursively checks vec/hash equality
Probably not very performant.
*/
int nasalStructEqual(naContext ctx, naRef a, naRef b);
class FGNasalListener : public SGPropertyChangeListener {
public:
FGNasalListener(SGPropertyNode* node, naRef code, FGNasalSys* nasal,

View file

@ -32,6 +32,8 @@
#include <Main/globals.hxx>
#include <Main/util.hxx>
#include <Scripting/NasalSys.hxx>
#include <Scripting/NasalSys_private.hxx>
#include <Main/fg_commands.hxx>
#include <simgear/nasal/cppbind/from_nasal.hxx>
@ -106,7 +108,7 @@ static naRef f_assert_equal(const nasal::CallContext& ctx )
naRef argB = ctx.requireArg<naRef>(1);
auto msg = ctx.getArg<string>(2, "assert_equal failed");
bool same = naEqual(argA, argB);
bool same = nasalStructEqual(ctx.c_ctx(), argA, argB);
if (!same) {
string aStr = ctx.from_nasal<string>(argA);
string bStr = ctx.from_nasal<string>(argB);
@ -142,6 +144,15 @@ static naRef f_assert_doubles_equal(const nasal::CallContext& ctx )
return naNil();
}
static naRef f_equal(const nasal::CallContext& ctx)
{
naRef argA = ctx.requireArg<naRef>(0);
naRef argB = ctx.requireArg<naRef>(1);
bool same = nasalStructEqual(ctx.c_ctx(), argA, argB);
return naNum(same);
}
//------------------------------------------------------------------------------
// commands
@ -187,6 +198,7 @@ naRef initNasalUnitTestInSim(naRef nasalGlobals, naContext c)
unitTest.set("fail", f_fail);
unitTest.set("assert_equal", f_assert_equal);
unitTest.set("assert_doubles_equal", f_assert_doubles_equal);
unitTest.set("equal", f_equal);
globals->get_commands()->addCommand("nasal-test", &command_executeNasalTest);
globals->get_commands()->addCommand("nasal-test-dir", &command_executeNasalTestDir);

View file

@ -30,6 +30,9 @@
#include <Main/globals.hxx>
#include <Main/util.hxx>
#include <Scripting/NasalSys.hxx>
#include <Scripting/NasalSys_private.hxx>
#include <cppunit/TestAssert.h>
#include <simgear/nasal/cppbind/from_nasal.hxx>
@ -50,9 +53,6 @@ static naRef f_assert(const nasal::CallContext& ctx )
auto msg = ctx.getArg<string>(1, "assert failed:");
CppUnit::Asserter::failIf(!pass, "assertion failed:" + msg, nasalSourceLine(ctx));
if (!pass) {
ctx.runtimeError(msg.c_str());
}
return naNil();
}
@ -62,8 +62,6 @@ static naRef f_fail(const nasal::CallContext& ctx )
CppUnit::Asserter::fail("assertion failed:" + msg,
nasalSourceLine(ctx));
ctx.runtimeError("Test failed: %s", msg.c_str());
return naNil();
}
@ -73,7 +71,7 @@ static naRef f_assert_equal(const nasal::CallContext& ctx )
naRef argB = ctx.requireArg<naRef>(1);
auto msg = ctx.getArg<string>(2, "assert_equal failed");
bool same = naEqual(argA, argB);
bool same = nasalStructEqual(ctx.c_ctx(), argA, argB);
if (!same) {
string aStr = ctx.from_nasal<string>(argA);
@ -81,12 +79,20 @@ static naRef f_assert_equal(const nasal::CallContext& ctx )
msg += "; expected:" + aStr + ", actual:" + bStr;
CppUnit::Asserter::fail(msg, nasalSourceLine(ctx));
ctx.runtimeError(msg.c_str());
}
return naNil();
}
static naRef f_equal(const nasal::CallContext& ctx)
{
naRef argA = ctx.requireArg<naRef>(0);
naRef argB = ctx.requireArg<naRef>(1);
bool same = nasalStructEqual(ctx.c_ctx(), argA, argB);
return naNum(same);
}
static naRef f_assert_doubles_equal(const nasal::CallContext& ctx )
{
double argA = ctx.requireArg<double>(0);
@ -99,7 +105,6 @@ static naRef f_assert_doubles_equal(const nasal::CallContext& ctx )
if (!same) {
msg += "; expected:" + std::to_string(argA) + ", actual:" + std::to_string(argB);
CppUnit::Asserter::fail(msg, nasalSourceLine(ctx));
ctx.runtimeError(msg.c_str());
}
return naNil();
@ -114,6 +119,7 @@ naRef initNasalUnitTestCppUnit(naRef nasalGlobals, naContext c)
unitTest.set("assert", f_assert);
unitTest.set("fail", f_fail);
unitTest.set("assert_equal", f_assert_equal);
unitTest.set("equal", f_equal);
unitTest.set("assert_doubles_equal", f_assert_doubles_equal);
return naNil();

View file

@ -20,21 +20,72 @@
#include "testNasalSys.hxx"
#include "test_suite/FGTestApi/testGlobals.hxx"
#include <Main/globals.hxx>
#include <Main/util.hxx>
#include <Scripting/NasalSys.hxx>
#include <Main/FGInterpolator.hxx>
// Set up function for each test.
void NasalSysTests::setUp()
{
FGTestApi::setUp::initTestGlobals("NasalSys");
fgInitAllowedPaths();
globals->get_props()->getNode("nasal", true);
globals->add_subsystem("prop-interpolator", new FGInterpolator, SGSubsystemMgr::INIT);
globals->get_subsystem_mgr()->bind();
globals->get_subsystem_mgr()->init();
globals->add_new_subsystem<FGNasalSys>(SGSubsystemMgr::INIT);
globals->get_subsystem_mgr()->postinit();
}
// Clean up after each test.
void NasalSysTests::tearDown()
{
FGTestApi::tearDown::shutdownTestGlobals();
}
// Test test
void NasalSysTests::testDummy()
void NasalSysTests::testStructEquality()
{
CPPUNIT_ASSERT(1 != 2);
bool ok = FGTestApi::executeNasal(R"(
var foo = {
"name": "Bob",
"size": [512, 512],
"mipmapping": 1.9
};
var bar = {
"name": "Bob",
"size": [512, 512],
"mipmapping": 1.9
};
unitTest.assert_equal(foo, bar);
append(bar.size, "Wowow");
unitTest.assert(unitTest.equal(foo, bar) == 0);
append(foo.size, "Wowow");
unitTest.assert_equal(foo, bar);
foo.wibble = 99.1;
unitTest.assert(unitTest.equal(foo, bar) == 0);
bar.wibble = 99;
unitTest.assert(unitTest.equal(foo, bar) == 0);
bar.wibble = 99.1;
unitTest.assert_equal(foo, bar);
)");
CPPUNIT_ASSERT(ok);
}

View file

@ -31,7 +31,7 @@ class NasalSysTests : public CppUnit::TestFixture
{
// Set up the test suite.
CPPUNIT_TEST_SUITE(NasalSysTests);
CPPUNIT_TEST(testDummy);
CPPUNIT_TEST(testStructEquality);
CPPUNIT_TEST_SUITE_END();
public:
@ -42,7 +42,7 @@ public:
void tearDown();
// The tests.
void testDummy();
void testStructEquality();
};
#endif // _FG_NASALSYS_UNIT_TESTS_HXX