Move some of the difference-transmission unit tests from lua to C++
This commit is contained in:
@@ -27,17 +27,6 @@ public:
|
|||||||
LuaSnap::LuaSnap() {
|
LuaSnap::LuaSnap() {
|
||||||
state_ = LuaCoreStack::newstate(eng::l_alloc);
|
state_ = LuaCoreStack::newstate(eng::l_alloc);
|
||||||
LuaExtStack LS(state_);
|
LuaExtStack LS(state_);
|
||||||
|
|
||||||
// Create the persist table and the unpersist table.
|
|
||||||
//
|
|
||||||
// These tables need to contain all C functions. Whenever
|
|
||||||
// the source module inserts a C function into the lua environment,
|
|
||||||
// it also needs to register the C function in these tables.
|
|
||||||
//
|
|
||||||
// I don't think anything else needs to go in these tables.
|
|
||||||
//
|
|
||||||
LS.rawset(LuaRegistry, "persist", LuaNewTable);
|
|
||||||
LS.rawset(LuaRegistry, "unpersist", LuaNewTable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaSnap::~LuaSnap() {
|
LuaSnap::~LuaSnap() {
|
||||||
|
|||||||
@@ -66,19 +66,22 @@ lua_State *LuaCoreStack::newstate (lua_Alloc allocf) {
|
|||||||
assert(L != nullptr);
|
assert(L != nullptr);
|
||||||
lua_atpanic(L, &panicf);
|
lua_atpanic(L, &panicf);
|
||||||
|
|
||||||
// We want all states to have a classes table and
|
// We want all states to have a classes table, a tangibles table,
|
||||||
// a tangibles table so that LS.makeclass and LS.maketan
|
// and persist/unpersist tables so that LS.makeclass, LS.maketan,
|
||||||
// work out-of-the-box.
|
// and eris work out-of-the-box.
|
||||||
|
LuaVar globtab;
|
||||||
|
LuaExtStack LS(L, globtab);
|
||||||
|
LS.rawset(LuaRegistry, "classes", LuaNewTable);
|
||||||
|
LS.rawset(LuaRegistry, "classnames", LuaNewTable);
|
||||||
|
LS.rawset(LuaRegistry, "tangibles", LuaNewTable);
|
||||||
|
LS.rawset(LuaRegistry, "persist", LuaNewTable);
|
||||||
|
LS.rawset(LuaRegistry, "unpersist", LuaNewTable);
|
||||||
|
|
||||||
|
// Tag the registry and global environment with their tabletypes.
|
||||||
|
LS.settabletype(LuaRegistry, LUA_TT_REGISTRY);
|
||||||
|
LS.getglobaltable(globtab);
|
||||||
|
LS.settabletype(globtab, LUA_TT_GLOBALENV);
|
||||||
|
|
||||||
lua_pushstring(L, "classes");
|
|
||||||
lua_newtable(L);
|
|
||||||
lua_rawset(L, LUA_REGISTRYINDEX);
|
|
||||||
lua_pushstring(L, "classnames");
|
|
||||||
lua_newtable(L);
|
|
||||||
lua_rawset(L, LUA_REGISTRYINDEX);
|
|
||||||
lua_pushstring(L, "tangibles");
|
|
||||||
lua_newtable(L);
|
|
||||||
lua_rawset(L, LUA_REGISTRYINDEX);
|
|
||||||
return L;
|
return L;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -262,6 +262,8 @@ public:
|
|||||||
class StreamBuffer : public eng::nevernew, public BaseBuffer<StreamBufferConfig> {
|
class StreamBuffer : public eng::nevernew, public BaseBuffer<StreamBufferConfig> {
|
||||||
public:
|
public:
|
||||||
using BaseBuffer::BaseBuffer;
|
using BaseBuffer::BaseBuffer;
|
||||||
|
StreamBuffer(const StreamBuffer &) = delete;
|
||||||
|
StreamBuffer &operator=(const StreamBuffer &) = delete;
|
||||||
|
|
||||||
void write_hashvalue(const util::HashValue &h) {
|
void write_hashvalue(const util::HashValue &h) {
|
||||||
write_uint64(h.first);
|
write_uint64(h.first);
|
||||||
|
|||||||
@@ -116,8 +116,8 @@ World::World(WorldType wt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prepare to manipulate the lua state.
|
// Prepare to manipulate the lua state.
|
||||||
LuaVar world, globtab;
|
LuaVar world;
|
||||||
LuaExtStack LS(state(), world, globtab);
|
LuaExtStack LS(state(), world);
|
||||||
|
|
||||||
// Put the world pointer into the lua registry.
|
// Put the world pointer into the lua registry.
|
||||||
World::store_global_pointer(state(), this);
|
World::store_global_pointer(state(), this);
|
||||||
@@ -125,14 +125,6 @@ World::World(WorldType wt) {
|
|||||||
// Clear the lthread state.
|
// Clear the lthread state.
|
||||||
clear_lthread_state();
|
clear_lthread_state();
|
||||||
|
|
||||||
// Set the tabletype of the registry.
|
|
||||||
LS.settabletype(LuaRegistry, LUA_TT_REGISTRY);
|
|
||||||
|
|
||||||
// Set the tabletype of the global environment.
|
|
||||||
LS.getglobaltable(globtab);
|
|
||||||
LS.settabletype(globtab, LUA_TT_GLOBALENV);
|
|
||||||
|
|
||||||
|
|
||||||
// Create the globaldb in the registry.
|
// Create the globaldb in the registry.
|
||||||
LS.rawset(LuaRegistry, "globaldb", LuaNewTable);
|
LS.rawset(LuaRegistry, "globaldb", LuaNewTable);
|
||||||
|
|
||||||
|
|||||||
@@ -8,13 +8,8 @@
|
|||||||
// World::patch_numbered_tables
|
// World::patch_numbered_tables
|
||||||
// World::patch_tangible_databases
|
// World::patch_tangible_databases
|
||||||
//
|
//
|
||||||
// It also contains these unit testing support routines:
|
|
||||||
//
|
|
||||||
// table.diffcompare
|
|
||||||
// table.diffapply
|
|
||||||
//
|
|
||||||
// This file also contains all the support code needed to implement
|
// This file also contains all the support code needed to implement
|
||||||
// this stuff.
|
// this stuff, plus the unit tests (unittests_difftab).
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@@ -142,52 +137,6 @@ static void transmit_value(LuaCoreStack &MLS, LuaSlot mval, LuaSlot mtnmap, Stre
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void transmit_value_debug_string(StreamBuffer *sb, eng::ostringstream &oss) {
|
|
||||||
int kind = sb->read_uint8();
|
|
||||||
switch (kind) {
|
|
||||||
case LUA_TBOOLEAN: {
|
|
||||||
bool b = sb->read_bool();
|
|
||||||
oss << (b ? "true":"false");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case LUA_TNUMBER: {
|
|
||||||
oss << sb->read_double();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case LUA_TSTRING: {
|
|
||||||
oss << sb->read_string();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case LUA_TLIGHTUSERDATA: {
|
|
||||||
LuaToken token(sb->read_uint64());
|
|
||||||
oss << "[" << token.str() << "]";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case LUA_TT_GENERAL: {
|
|
||||||
oss << "table " << sb->read_int32();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case LUA_TT_CLASS: {
|
|
||||||
oss << "class " << sb->read_string();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case LUA_TT_TANGIBLE: {
|
|
||||||
oss << "tan " << sb->read_int64();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case LUA_TT_GLOBALENV: {
|
|
||||||
oss << "globals";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case LUA_TNIL: {
|
|
||||||
oss << "nil";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
assert(false); // Should not get here.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool diff_tables(LuaCoreStack &SLS0, LuaSlot stnmap, LuaSlot stab,
|
static bool diff_tables(LuaCoreStack &SLS0, LuaSlot stnmap, LuaSlot stab,
|
||||||
LuaCoreStack &MLS0, LuaSlot mtnmap, LuaSlot mtab,
|
LuaCoreStack &MLS0, LuaSlot mtnmap, LuaSlot mtab,
|
||||||
bool cmeta, StreamBuffer *sb) {
|
bool cmeta, StreamBuffer *sb) {
|
||||||
@@ -240,24 +189,6 @@ static bool diff_tables(LuaCoreStack &SLS0, LuaSlot stnmap, LuaSlot stab,
|
|||||||
return (nupdates > 0);
|
return (nupdates > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static eng::string diff_tables_debug_string(StreamBuffer *sb) {
|
|
||||||
eng::vector<eng::string> sorted;
|
|
||||||
eng::ostringstream oss;
|
|
||||||
int ndiffs = sb->read_int32();
|
|
||||||
for (int i = 0; i < ndiffs; i++) {
|
|
||||||
transmit_value_debug_string(sb, oss);
|
|
||||||
oss << "=";
|
|
||||||
transmit_value_debug_string(sb, oss);
|
|
||||||
sorted.push_back(oss.str());
|
|
||||||
oss.str("");
|
|
||||||
}
|
|
||||||
std::sort(sorted.begin(), sorted.end());
|
|
||||||
for (const eng::string &s : sorted) {
|
|
||||||
oss << s << ";";
|
|
||||||
}
|
|
||||||
return oss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_transmitted_value(LuaCoreStack &LS, LuaSlot tangibles, LuaSlot ntmap, LuaSlot target, StreamBuffer *sb, const char *dbinfo, DebugCollector *dbc) {
|
static void set_transmitted_value(LuaCoreStack &LS, LuaSlot tangibles, LuaSlot ntmap, LuaSlot target, StreamBuffer *sb, const char *dbinfo, DebugCollector *dbc) {
|
||||||
int kind = sb->read_uint8();
|
int kind = sb->read_uint8();
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
@@ -441,82 +372,302 @@ void World::diff_tangible_databases(const IdVector &basis, lua_State *master, St
|
|||||||
sb->overwrite_int32(write_count_after, nmodified);
|
sb->overwrite_int32(write_count_after, nmodified);
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaDefine(table_diffcompare, "mtnmap,mtab,stnmap,stab", "for unit testing only") {
|
////////////////////////////////////////////////////////////////////
|
||||||
LuaArg mtnmap, mtab, mstnmap, mstab;
|
//
|
||||||
LuaRet dbgstring;
|
// Unit Testing Framework for Table Diff
|
||||||
LuaVar tthread;
|
//
|
||||||
LuaDefStack MLS(L, mtnmap, mtab, mstnmap, mstab, dbgstring, tthread);
|
// The function test_diffcompare creates a standalone lua environment,
|
||||||
// Check the arguments.
|
// evaluates two lua expressions to build a master table and a synch
|
||||||
MLS.cktable(mtnmap, "mtnmap");
|
// table, runs diff_tables, and returns the result as a debug string.
|
||||||
MLS.cktable(mstnmap, "mstnmap");
|
//
|
||||||
MLS.cktable(mtab, "mtab");
|
////////////////////////////////////////////////////////////////////
|
||||||
MLS.cktable(mstab, "mstab");
|
|
||||||
|
|
||||||
// Create a temporary thread to be the 'synch model'. We'll use the
|
#include "source.hpp"
|
||||||
// existing thread as the 'master model'. Move two tables to the synch thread.
|
#include "traceback.hpp"
|
||||||
lua_State *synch = lua_newthread(L);
|
#include "pprint.hpp"
|
||||||
lua_replace(L, tthread.index());
|
#include "wrap-sstream.hpp"
|
||||||
lua_pushvalue(L, mstnmap.index());
|
|
||||||
lua_pushvalue(L, mstab.index());
|
|
||||||
lua_xmove(L, synch, 2);
|
|
||||||
LuaArg stnmap,stab;
|
|
||||||
LuaDefStack SLS(synch, stnmap, stab);
|
|
||||||
|
|
||||||
// Call tablecmp_diff.
|
class DiffTester {
|
||||||
|
lua_State *master_L_ = nullptr;
|
||||||
|
lua_State *synch_L_ = nullptr;
|
||||||
|
lua_State *caller_;
|
||||||
|
SourceDB master_sdb_;
|
||||||
|
SourceDB synch_sdb_;
|
||||||
|
|
||||||
|
static void transmit_value_debug_string(StreamBuffer *sb, eng::ostringstream &oss) {
|
||||||
|
int kind = sb->read_uint8();
|
||||||
|
switch (kind) {
|
||||||
|
case LUA_TBOOLEAN: {
|
||||||
|
bool b = sb->read_bool();
|
||||||
|
oss << (b ? "true":"false");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TNUMBER: {
|
||||||
|
oss << sb->read_double();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TSTRING: {
|
||||||
|
oss << sb->read_string();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TLIGHTUSERDATA: {
|
||||||
|
LuaToken token(sb->read_uint64());
|
||||||
|
oss << "[" << token.str() << "]";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TT_GENERAL: {
|
||||||
|
oss << "tab" << sb->read_int32();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TT_CLASS: {
|
||||||
|
oss << "class " << sb->read_string();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TT_TANGIBLE: {
|
||||||
|
oss << "tan " << sb->read_int64();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TT_GLOBALENV: {
|
||||||
|
oss << "globals";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case LUA_TNIL: {
|
||||||
|
oss << "nil";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
assert(false); // Should not get here.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static eng::string diff_tables_debug_string(StreamBuffer *sb) {
|
||||||
|
eng::vector<eng::string> sorted;
|
||||||
|
eng::ostringstream oss;
|
||||||
|
int ndiffs = sb->read_int32();
|
||||||
|
for (int i = 0; i < ndiffs; i++) {
|
||||||
|
transmit_value_debug_string(sb, oss);
|
||||||
|
oss << "=";
|
||||||
|
transmit_value_debug_string(sb, oss);
|
||||||
|
sorted.push_back(oss.str());
|
||||||
|
oss.str("");
|
||||||
|
}
|
||||||
|
std::sort(sorted.begin(), sorted.end());
|
||||||
|
for (const eng::string &s : sorted) {
|
||||||
|
oss << s << ";";
|
||||||
|
}
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
DiffTester(lua_State *caller) : caller_(caller) {
|
||||||
|
SourceDB::register_lua_builtins();
|
||||||
|
master_L_ = LuaCoreStack::newstate(eng::l_alloc);
|
||||||
|
synch_L_ = LuaCoreStack::newstate(eng::l_alloc);
|
||||||
|
master_sdb_.init(master_L_);
|
||||||
|
synch_sdb_.init(synch_L_);
|
||||||
|
}
|
||||||
|
|
||||||
|
~DiffTester() {
|
||||||
|
if (master_L_) lua_close(master_L_);
|
||||||
|
if (synch_L_) lua_close(synch_L_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create stock test tables and their maps in a single lua environment.
|
||||||
|
// Sets up globals tab1-tab9 and populates the tnmap and ntmap.
|
||||||
|
void create_stock_tables(LuaCoreStack &LS, LuaSlot tnmap, LuaSlot ntmap) {
|
||||||
|
lua_State *L = LS.state();
|
||||||
|
LuaVar globtab;
|
||||||
|
LuaExtStack LS2(L, globtab);
|
||||||
|
LS.set(tnmap, LuaNewTable);
|
||||||
|
LS.set(ntmap, LuaNewTable);
|
||||||
|
LS.getglobaltable(globtab);
|
||||||
|
for (int i = 1; i <= 9; i++) {
|
||||||
|
LuaVar tabi;
|
||||||
|
LuaExtStack LS3(L, tabi);
|
||||||
|
LS.set(tabi, LuaNewTable);
|
||||||
|
LS.rawset(tabi, 1, util::ss("tab", i));
|
||||||
|
LS.rawset(globtab, util::ss("tab", i), tabi);
|
||||||
|
LS.rawset(tnmap, tabi, i);
|
||||||
|
LS.rawset(ntmap, i, tabi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pretty-print a lua value to a string.
|
||||||
|
eng::string pprint(LuaCoreStack &LS, LuaSlot val) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
PrettyPrint::Indented().print(LS, val, &oss);
|
||||||
|
return eng::string(oss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate a lua expression and store the result in the given slot.
|
||||||
|
void eval(LuaCoreStack &LS, LuaSlot dest, const char *expr, const char *name) {
|
||||||
|
lua_State *L = LS.state();
|
||||||
|
LuaVar closure;
|
||||||
|
LuaExtStack LS2(L, closure);
|
||||||
|
eng::string err = LS2.load(closure, util::ss("return ", expr), name);
|
||||||
|
LuaAssert(caller_, err.empty());
|
||||||
|
lua_pushvalue(L, closure.index());
|
||||||
|
err = traceback_pcall(L, 0, 1);
|
||||||
|
LuaAssert(caller_, err.empty());
|
||||||
|
lua_replace(L, dest.index());
|
||||||
|
}
|
||||||
|
|
||||||
|
eng::string diffcompare(const char *master_expr, const char *synch_expr) {
|
||||||
|
assert(lua_gettop(master_L_) == 0);
|
||||||
|
assert(lua_gettop(synch_L_) == 0);
|
||||||
|
|
||||||
|
LuaVar mtnmap, mntmap, mtab;
|
||||||
|
LuaExtStack MLS(master_L_, mtnmap, mntmap, mtab);
|
||||||
|
LuaVar stnmap, sntmap, stab;
|
||||||
|
LuaExtStack SLS(synch_L_, stnmap, sntmap, stab);
|
||||||
|
|
||||||
|
create_stock_tables(MLS, mtnmap, mntmap);
|
||||||
|
create_stock_tables(SLS, stnmap, sntmap);
|
||||||
|
eval(MLS, mtab, master_expr, "master");
|
||||||
|
eval(SLS, stab, synch_expr, "synch");
|
||||||
|
|
||||||
|
// Run diff_tables.
|
||||||
StreamBuffer sb;
|
StreamBuffer sb;
|
||||||
diff_tables(SLS, stnmap, stab, MLS, mtnmap, mtab, true, &sb);
|
diff_tables(SLS, stnmap, stab, MLS, mtnmap, mtab, true, &sb);
|
||||||
|
|
||||||
// Convert the output to a debug string.
|
return diff_tables_debug_string(&sb);
|
||||||
MLS.set(dbgstring, diff_tables_debug_string(&sb));
|
|
||||||
return MLS.result();
|
|
||||||
}
|
|
||||||
|
|
||||||
LuaDefine(table_diffapply, "mtnmap,mtab,mstab", "for unit testing only") {
|
|
||||||
LuaArg mtnmap, mtab, mstab;
|
|
||||||
LuaRet eql, eqlstr, rtab;
|
|
||||||
LuaVar tthread, tangibles, mntmap, key, val;
|
|
||||||
LuaDefStack MLS(L, mtnmap, mtab, mstab, eql, eqlstr, rtab, tthread, tangibles, mntmap, key, val);
|
|
||||||
// Check the arguments.
|
|
||||||
MLS.cktable(mtnmap, "mtnmap");
|
|
||||||
MLS.cktable(mtab, "mtab");
|
|
||||||
MLS.cktable(mstab, "mstab");
|
|
||||||
|
|
||||||
// Get the tangibles map.
|
|
||||||
MLS.rawget(tangibles, LuaRegistry, "tangibles");
|
|
||||||
|
|
||||||
// Invert the tnmap to make the ntmap.
|
|
||||||
MLS.set(mntmap, LuaNewTable);
|
|
||||||
MLS.set(key, LuaNil);
|
|
||||||
while (MLS.next(mtnmap, key, val)) {
|
|
||||||
MLS.rawset(mntmap, val, key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a temporary thread to be the 'synch model'. We'll use the
|
bool diffapply(const char *master_expr, const char *synch_expr, bool verbose = false) {
|
||||||
// existing thread as the 'master model'.
|
assert(lua_gettop(master_L_) == 0);
|
||||||
lua_State *synch = lua_newthread(L);
|
assert(lua_gettop(synch_L_) == 0);
|
||||||
lua_replace(L, tthread.index());
|
|
||||||
lua_pushvalue(L, mtnmap.index());
|
|
||||||
lua_pushvalue(L, mstab.index());
|
|
||||||
lua_xmove(L, synch, 2);
|
|
||||||
LuaArg stnmap, stab;
|
|
||||||
LuaDefStack SLS(synch, stnmap, stab);
|
|
||||||
|
|
||||||
// Call diff_tables and patch_tables
|
LuaVar mtnmap, mntmap, mtab;
|
||||||
|
LuaExtStack MLS(master_L_, mtnmap, mntmap, mtab);
|
||||||
|
LuaVar stnmap, sntmap, stab, tangibles;
|
||||||
|
LuaExtStack SLS(synch_L_, stnmap, sntmap, stab, tangibles);
|
||||||
|
|
||||||
|
create_stock_tables(MLS, mtnmap, mntmap);
|
||||||
|
create_stock_tables(SLS, stnmap, sntmap);
|
||||||
|
eval(MLS, mtab, master_expr, "master");
|
||||||
|
eval(SLS, stab, synch_expr, "synch");
|
||||||
|
|
||||||
|
// Get the tangibles map from the synch environment.
|
||||||
|
SLS.rawget(tangibles, LuaRegistry, "tangibles");
|
||||||
|
|
||||||
|
// Diff and patch.
|
||||||
StreamBuffer sb;
|
StreamBuffer sb;
|
||||||
diff_tables(SLS, stnmap, stab, MLS, mtnmap, mtab, true, &sb);
|
diff_tables(SLS, stnmap, stab, MLS, mtnmap, mtab, true, &sb);
|
||||||
|
if (verbose) {
|
||||||
patch_table(MLS, tangibles, mntmap, mstab, &sb, nullptr);
|
StreamBuffer sb_copy(sb.view());
|
||||||
|
eng::string diff_str = diff_tables_debug_string(&sb_copy);
|
||||||
bool eq = table_equal(MLS, mstab, mtab);
|
printf("diffapply: master_expr = %s\n", master_expr);
|
||||||
MLS.set(eql, eq);
|
printf("diffapply: synch_expr = %s\n", synch_expr);
|
||||||
if (eq) {
|
printf("diffapply: diff = %s\n", diff_str.c_str());
|
||||||
MLS.set(eqlstr, "tables equal");
|
|
||||||
} else {
|
|
||||||
MLS.set(eqlstr, "tables were supposed to be equal");
|
|
||||||
}
|
}
|
||||||
MLS.set(rtab, stab);
|
patch_table(SLS, tangibles, sntmap, stab, &sb, nullptr);
|
||||||
|
|
||||||
return MLS.result();
|
// Check equality by pretty-printing both tables and comparing.
|
||||||
|
eng::string master_str = pprint(MLS, mtab);
|
||||||
|
eng::string synch_str = pprint(SLS, stab);
|
||||||
|
if (verbose) {
|
||||||
|
printf("diffapply: master pprint (%d chars) = %s\n", (int)master_str.size(), master_str.c_str());
|
||||||
|
printf("diffapply: synch pprint (%d chars) = %s\n", (int)synch_str.size(), synch_str.c_str());
|
||||||
|
}
|
||||||
|
return master_str == synch_str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
LuaDefine(unittests_difftab, "", "unit tests for table diff") {
|
||||||
|
DiffTester dt(L);
|
||||||
|
|
||||||
|
// No differences in these simple-valued tables.
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a=true}", "{a=true}"), "");
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a=5}", "{a=5}"), "");
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a='foo'}", "{a='foo'}"), "");
|
||||||
|
|
||||||
|
// Test transmission of missing simple values.
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a=true}", "{}"), "a=true;");
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a=5}", "{}"), "a=5;");
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a='foo'}", "{}"), "a=foo;");
|
||||||
|
|
||||||
|
// Test the replacement of simple values.
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a=true}", "{a=false}"), "a=true;");
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a=5}", "{a=4}"), "a=5;");
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a='foo'}", "{a='bar'}"), "a=foo;");
|
||||||
|
|
||||||
|
// Test the clearing of values.
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{}", "{a=true}"), "a=nil;");
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{}", "{a=5}"), "a=nil;");
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{}", "{a='foo'}"), "a=nil;");
|
||||||
|
|
||||||
|
// Try boolean keys.
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{[true]=3}", "{}"), "true=3;");
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{}", "{[true]=3}"), "true=nil;");
|
||||||
|
|
||||||
|
// Try number keys.
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{[7]=3}", "{}"), "7=3;");
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{}", "{[7]=3}"), "7=nil;");
|
||||||
|
|
||||||
|
// Try a table with multiple keys.
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a=4, b=5, c=6}", "{b=5, c=7, d=8}"), "a=4;c=6;d=nil;");
|
||||||
|
|
||||||
|
// Nonsortable keys should be ignored (no diffs).
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{[{}]=3}", "{}"), "");
|
||||||
|
|
||||||
|
// Numbered tables: matching pairs produce no diff.
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a=tab1}", "{a=tab1}"), "");
|
||||||
|
|
||||||
|
// Numbered tables: missing in synch.
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a=tab1}", "{}"), "a=tab1;");
|
||||||
|
|
||||||
|
// Numbered tables: wrong table number.
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a=tab1}", "{a=tab2}"), "a=tab1;");
|
||||||
|
|
||||||
|
// Numbered tables: replaced by simple value.
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a=3}", "{a=tab1}"), "a=3;");
|
||||||
|
|
||||||
|
// Numbered tables: cleared.
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{}", "{a=tab1}"), "a=nil;");
|
||||||
|
|
||||||
|
// Unnumbered tables are forced to nil.
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a={}}", "{}"), "");
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a={}}", "{a=3}"), "a=nil;");
|
||||||
|
|
||||||
|
// Class values.
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a=deque}", "{}"), "a=class deque;");
|
||||||
|
|
||||||
|
// Global environment.
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{a=_G}", "{}"), "a=globals;");
|
||||||
|
|
||||||
|
// Metatable: set, match, clear.
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("setmetatable({}, tab1)", "{}"), "nil=tab1;");
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("setmetatable({}, tab1)", "setmetatable({}, tab1)"), "");
|
||||||
|
LuaAssertStrEq(L, dt.diffcompare("{}", "setmetatable({}, tab1)"), "nil=nil;");
|
||||||
|
|
||||||
|
// Diff-apply: verify simple values.
|
||||||
|
LuaAssert(L, dt.diffapply("{a=1}", "{}"));
|
||||||
|
LuaAssert(L, dt.diffapply("{[true]='foo'}", "{}"));
|
||||||
|
LuaAssert(L, dt.diffapply("{[3]=false}", "{}"));
|
||||||
|
|
||||||
|
// Diff-apply: multiple simple values.
|
||||||
|
LuaAssert(L, dt.diffapply("{a=1, b=2, c=3}", "{}"));
|
||||||
|
|
||||||
|
// Diff-apply: remove or replace wrong values.
|
||||||
|
LuaAssert(L, dt.diffapply("{a=1, b=2}", "{b=3, c=4}"));
|
||||||
|
|
||||||
|
// Diff-apply: table containing a numbered table.
|
||||||
|
LuaAssert(L, dt.diffapply("{a=tab1, b=tab2}", "{}"));
|
||||||
|
|
||||||
|
// Diff-apply: table containing a class.
|
||||||
|
LuaAssert(L, dt.diffapply("{a=deque, b=table}", "{}"));
|
||||||
|
|
||||||
|
// Diff-apply: table containing the global environment.
|
||||||
|
LuaAssert(L, dt.diffapply("{a=_G}", "{}"));
|
||||||
|
|
||||||
|
// Diff-apply: unnumbered tables are forced to nil.
|
||||||
|
LuaAssert(L, !dt.diffapply("{a={}}", "{a=3}"));
|
||||||
|
|
||||||
|
// Diff-apply: set metatable.
|
||||||
|
LuaAssert(L, dt.diffapply("setmetatable({}, tab1)", "{}"));
|
||||||
|
|
||||||
|
// Diff-apply: clear metatable.
|
||||||
|
LuaAssert(L, dt.diffapply("{}", "setmetatable({}, tab1)"));
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,5 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
ut-table.lua
|
ut-table.lua
|
||||||
ut-tablecmp.lua
|
|
||||||
hotkeys.lua
|
hotkeys.lua
|
||||||
login.lua
|
login.lua
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
|
|
||||||
-- the tdc function calculates diffs, and returns those
|
|
||||||
-- diffs as a human-readable string.
|
|
||||||
local tdc = table.diffcompare
|
|
||||||
|
|
||||||
-- the tda function calculates diffs, applies the diffs to the second
|
|
||||||
-- table, and then returns true if the second table equals the first.
|
|
||||||
local tda = table.diffapply
|
|
||||||
|
|
||||||
function unittests.diffcompare()
|
|
||||||
local mtab = nil
|
|
||||||
local rtab = nil
|
|
||||||
|
|
||||||
-- No differences in these simple-valued tables.
|
|
||||||
assert(tdc({}, {a=true}, {}, {a=true}) == "")
|
|
||||||
assert(tdc({}, {a=5}, {}, {a=5}) == "");
|
|
||||||
assert(tdc({}, {a="foo"}, {}, {a="foo"}) == "")
|
|
||||||
|
|
||||||
-- Test transmission of missing simple values.
|
|
||||||
assert(tdc({}, {a=true}, {}, {}) == "a=true;")
|
|
||||||
assert(tdc({}, {a=5}, {}, {}) == "a=5;");
|
|
||||||
assert(tdc({}, {a="foo"}, {}, {}) == "a=foo;")
|
|
||||||
|
|
||||||
-- Test the replacement of simple values.
|
|
||||||
assert(tdc({}, {a=true}, {}, {a=false}) == "a=true;")
|
|
||||||
assert(tdc({}, {a=5}, {}, {a=4}) == "a=5;");
|
|
||||||
assert(tdc({}, {a="foo"}, {}, {a="bar"}) == "a=foo;")
|
|
||||||
|
|
||||||
-- Test the clearing of values.
|
|
||||||
assert(tdc({}, {}, {}, {a=true}) == "a=nil;")
|
|
||||||
assert(tdc({}, {}, {}, {a=5}) == "a=nil;");
|
|
||||||
assert(tdc({}, {}, {}, {a="foo"}) == "a=nil;")
|
|
||||||
|
|
||||||
-- Try boolean keys.
|
|
||||||
assert(tdc({}, {[true]=3}, {}, {}) == "true=3;")
|
|
||||||
assert(tdc({}, {}, {}, {[true]=3}) == "true=nil;")
|
|
||||||
|
|
||||||
-- Try number keys.
|
|
||||||
assert(tdc({}, {[7]=3}, {}, {}) == "7=3;")
|
|
||||||
assert(tdc({}, {}, {}, {[7]=3}) == "7=nil;")
|
|
||||||
|
|
||||||
-- Try a table with multiple keys.
|
|
||||||
assert(tdc({}, {a=4, b=5, c=6}, {}, {b=5, c=7, d=8}) == "a=4;c=6;d=nil;")
|
|
||||||
|
|
||||||
-- Nonsortable keys should be ignored (no diffs).
|
|
||||||
assert(tdc({}, {[{}]=3}, {}, {}) == "")
|
|
||||||
|
|
||||||
-- Try a table containing a class.
|
|
||||||
assert(tdc({}, {a=deque}, {}, {}) == "a=class deque;")
|
|
||||||
|
|
||||||
-- Try a table containing a pointer to the global environment.
|
|
||||||
assert(tdc({}, {a=_G}, {}, {}) == "a=globals;")
|
|
||||||
|
|
||||||
-- Set up some numbered tables for tests involving such.
|
|
||||||
local mtab10 = {}
|
|
||||||
local stab10 = {}
|
|
||||||
local mtnmap = {}
|
|
||||||
local stnmap = {}
|
|
||||||
mtnmap[mtab10] = 10
|
|
||||||
stnmap[stab10] = 10
|
|
||||||
|
|
||||||
-- confirm that numbered tables are being transmitted.
|
|
||||||
assert(tdc(mtnmap, {a=mtab10}, stnmap, {}) == "a=table 10;")
|
|
||||||
assert(tdc(mtnmap, {a=mtab10}, stnmap, {a=stab10}) == "")
|
|
||||||
assert(tdc(mtnmap, {a=3}, stnmap, {a=stab10}) == "a=3;")
|
|
||||||
assert(tdc(mtnmap, {}, stnmap, {a=stab10}) == "a=nil;")
|
|
||||||
|
|
||||||
-- confirm that unnumbered tables are forced to nil.
|
|
||||||
assert(tdc(mtnmap, {a={}}, stnmap, {}) == "")
|
|
||||||
assert(tdc(mtnmap, {a={}}, stnmap, {a=3}) == "a=nil;")
|
|
||||||
|
|
||||||
-- transmit a correction to the metatable.
|
|
||||||
mtab={}
|
|
||||||
stab={}
|
|
||||||
setmetatable(mtab, mtab10)
|
|
||||||
setmetatable(stab, stab10)
|
|
||||||
assert(tdc(mtnmap, mtab, stnmap, {}) == "nil=table 10;")
|
|
||||||
assert(tdc(mtnmap, mtab, stnmap, stab) == "")
|
|
||||||
assert(tdc(mtnmap, {}, stnmap, stab) == "nil=nil;")
|
|
||||||
|
|
||||||
-- we're not going to test tangibles
|
|
||||||
-- creating tangibles here is too difficult.
|
|
||||||
end
|
|
||||||
|
|
||||||
function unittests.diffapply()
|
|
||||||
local tab10={id=tab10}
|
|
||||||
local tab11={id=tab11}
|
|
||||||
local tnmap={}
|
|
||||||
tnmap[tab10] = 10
|
|
||||||
tnmap[tab11] = 11
|
|
||||||
local mtab=nil
|
|
||||||
local stab=nil
|
|
||||||
|
|
||||||
-- verify some simple values.
|
|
||||||
assert(tda(tnmap, {a=1}, {}))
|
|
||||||
assert(tda(tnmap, {[true]="foo"}, {}))
|
|
||||||
assert(tda(tnmap, {[3]=false}, {}))
|
|
||||||
|
|
||||||
-- verify a table with multiple simple values.
|
|
||||||
assert(tda(tnmap, {a=1, b=2, c=3}, {}))
|
|
||||||
|
|
||||||
-- verify that it can remove or replace wrong values.
|
|
||||||
assert(tda(tnmap, {a=1,b=2}, {b=3,c=4}))
|
|
||||||
|
|
||||||
-- verify a table containing another table.
|
|
||||||
assert(tda(tnmap, {a=tab10, b=tab11}, {}))
|
|
||||||
|
|
||||||
-- verify a table containing a class.
|
|
||||||
assert(tda(tnmap, {a=deque, b=table}, {}))
|
|
||||||
|
|
||||||
-- verify a table containing the global environment.
|
|
||||||
assert(tda(tnmap, {a=_G}, {}))
|
|
||||||
|
|
||||||
-- Unnumbered tables should be forced to NIL.
|
|
||||||
rtab={a=3}
|
|
||||||
assert(not tda({}, {a={}}, rtab))
|
|
||||||
assert(rtab.a == nil)
|
|
||||||
|
|
||||||
-- transmit a correction to the metatable
|
|
||||||
mtab={}
|
|
||||||
rtab={}
|
|
||||||
setmetatable(mtab, tab10)
|
|
||||||
assert(tda(tnmap, mtab, rtab))
|
|
||||||
assert(getmetatable(rtab) == tab10)
|
|
||||||
|
|
||||||
-- transmit a clearing of the metatable
|
|
||||||
mtab={}
|
|
||||||
rtab={}
|
|
||||||
setmetatable(rtab, tab10)
|
|
||||||
assert(tda(tnmap, mtab, rtab))
|
|
||||||
assert(getmetatable(rtab) == nil)
|
|
||||||
|
|
||||||
-- don't test tangibles.
|
|
||||||
end
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user