Support for debug_string method for testing

This commit is contained in:
2021-07-21 00:50:06 -04:00
parent 4ba0471bde
commit c2a69b5b23
6 changed files with 197 additions and 150 deletions

View File

@@ -3,6 +3,8 @@
#include "luastack.hpp"
#include "animqueue.hpp"
#include "streambuffer.hpp"
#include <ostream>
#include <sstream>
AnimStep::AnimStep() {
clear();
@@ -252,6 +254,75 @@ bool AnimStep::echoes(const AnimStep &prev) const {
return true;
}
std::string AnimStep::debug_string() const {
std::ostringstream oss;
oss << "id=" << id();
oss << " action=" << action();
if (has_plane()) {
oss << " plane=" << plane();
}
if (has_x()) {
oss << " x=" << xyz().x;
}
if (has_y()) {
oss << " y=" << xyz().y;
}
if (has_z()) {
oss << " z=" << xyz().z;
}
if (has_facing()) {
oss << " facing=" << facing();
}
if (has_graphic()) {
oss << " graphic=" << graphic();
}
return oss.str();
}
bool AnimStep::from_string(const std::string &config) {
clear();
util::stringvec parts = util::split(config, ' ');
for (int i = 0; i < int(parts.size()); i++) {
const std::string &part = parts[i];
if (part == "") continue;
util::stringvec lr = util::split(part, '=');
if (lr.size() != 2) return false;
const std::string &key = lr[0];
const std::string &val = lr[1];
if (key == "action") {
action_ = val;
} else if (key == "id") {
int64_t id = util::strtoint(val, -1);
if (id < 0) return false;
id_ = id;
} else if (key == "plane") {
set_plane(val);
} else if (key == "x") {
double v = util::strtodouble(val);
if (std::isnan(v)) return false;
set_x(v);
} else if (key == "y") {
double v = util::strtodouble(val);
if (std::isnan(v)) return false;
set_y(v);
} else if (key == "z") {
double v = util::strtodouble(val);
if (std::isnan(v)) return false;
set_z(v);
} else if (key == "facing") {
double v = util::strtodouble(val);
if (std::isnan(v)) return false;
set_facing(v);
} else if (key == "graphic") {
set_graphic(val);
} else {
return false;
}
}
return true;
}
AnimQueue::AnimQueue(util::WorldType wt) {
world_type_ = wt;
size_limit_ = 10; // Default size limit.
@@ -293,11 +364,16 @@ void AnimQueue::clear_steps() {
mutated();
}
void AnimQueue::set_size_limit(int n) {
void AnimQueue::full_clear_and_set_limit(int n) {
assert(n >= 1);
if (size_limit_ == n) return;
clear_steps();
size_limit_ = n;
while (int(steps_.size()) > size_limit_) {
version_number_ = 1;
}
void AnimQueue::set_limit(int n) {
assert(n >= 1);
size_limit_ = n;
while (int(steps_.size()) > n) {
steps_.pop_front();
}
steps_.front().keep_state_only();
@@ -345,6 +421,16 @@ bool AnimQueue::valid() const {
return true;
}
std::string AnimQueue::debug_string() const {
std::ostringstream oss;
oss << "version=" << version_number();
oss << "; limit=" << size_limit();
for (int i = 0; i < int(size()); i++) {
oss << "; " << nth(i).debug_string();
}
return oss.str();
}
void AnimQueue::serialize(StreamBuffer *sb) {
assert(valid()); // can't serialize an invalid animqueue.
sb->write_int64(version_number_);
@@ -469,163 +555,98 @@ LuaDefine(unittests_animqueue, "c") {
AnimQueue aq(util::WORLD_TYPE_MASTER);
StreamBuffer sb;
AnimQueue aqds(util::WORLD_TYPE_S_SYNC);
aq.set_size_limit(3);
LuaAssert(L, aq.valid());
LuaAssert(L, aq.size() == 1);
const AnimStep *st = &aq.nth(0);
LuaAssert(L, st->id() == 0);
LuaAssert(L, st->action() == "");
LuaAssert(L, st->bits() == AnimStep::HAS_EVERYTHING);
LuaAssert(L, st->facing() == 0.0);
LuaAssert(L, st->xyz() == util::XYZ(0,0,0));
LuaAssert(L, st->graphic() == "");
LuaAssert(L, st->plane() == "");
// Test the step setters.
stp.clear();
LuaAssertStrEq(L, stp.debug_string(), "id=0 action=");
stp.set_facing(180);
LuaAssert(L, stp.facing() == 180);
LuaAssert(L, stp.bits() == AnimStep::HAS_FACING);
LuaAssertStrEq(L, stp.debug_string(), "id=0 action= facing=180");
stp.set_x(3);
LuaAssert(L, stp.xyz() == util::XYZ(3, 0, 0));
LuaAssert(L, stp.bits() == (AnimStep::HAS_FACING | AnimStep::HAS_X));
LuaAssertStrEq(L, stp.debug_string(), "id=0 action= x=3 facing=180");
stp.set_y(4);
LuaAssert(L, stp.xyz() == util::XYZ(3, 4, 0));
LuaAssert(L, stp.bits() == (AnimStep::HAS_FACING | AnimStep::HAS_X | AnimStep::HAS_Y));
LuaAssertStrEq(L, stp.debug_string(), "id=0 action= x=3 y=4 facing=180");
stp.set_z(5);
LuaAssert(L, stp.xyz() == util::XYZ(3, 4, 5));
LuaAssert(L, stp.bits() == (AnimStep::HAS_FACING | AnimStep::HAS_X | AnimStep::HAS_Y | AnimStep::HAS_Z));
LuaAssertStrEq(L, stp.debug_string(), "id=0 action= x=3 y=4 z=5 facing=180");
stp.set_plane("somewhere");
LuaAssert(L, stp.plane() == "somewhere");
LuaAssert(L, stp.bits() == (AnimStep::HAS_FACING | AnimStep::HAS_XYZ | AnimStep::HAS_PLANE));
LuaAssertStrEq(L, stp.debug_string(), "id=0 action= plane=somewhere x=3 y=4 z=5 facing=180");
stp.set_graphic("something");
LuaAssert(L, stp.graphic() == "something");
LuaAssert(L, stp.bits() == (AnimStep::HAS_FACING | AnimStep::HAS_XYZ | AnimStep::HAS_PLANE | AnimStep::HAS_GRAPHIC));
LuaAssertStrEq(L, stp.debug_string(), "id=0 action= plane=somewhere x=3 y=4 z=5 facing=180 graphic=something");
// Add a step.
stp.clear();
stp.set_action("walk");
aq.add(12345, stp);
// Test the step parser.
LuaAssert(L, stp.from_string("id=123 action=walk x=1 y=2 z=3 facing=4 plane=p graphic=g"));
LuaAssertStrEq(L, stp.debug_string(), "id=123 action=walk plane=p x=1 y=2 z=3 facing=4 graphic=g");
// Test a blank queue.
aq.full_clear_and_set_limit(3);
LuaAssert(L, aq.valid());
LuaAssert(L, aq.size() == 2);
st = &aq.nth(1);
LuaAssert(L, st->id() == 12345);
LuaAssert(L, st->action() == "walk");
LuaAssert(L, st->bits() == 0);
LuaAssert(L, st->facing() == 0.0);
LuaAssert(L, st->xyz() == util::XYZ(0,0,0));
LuaAssert(L, st->graphic() == "");
LuaAssert(L, st->plane() == "");
LuaAssertStrEq(L, aq.debug_string(), "version=1; limit=3; id=0 action= plane= x=0 y=0 z=0 facing=0 graphic=");
// Add a step to a queue.
aq.full_clear_and_set_limit(3);
LuaAssert(L, stp.from_string("action=walk"));
aq.add(12345, stp);
LuaAssertStrEq(L, aq.debug_string(),
"version=2; limit=3; "
"id=0 action= plane= x=0 y=0 z=0 facing=0 graphic=; "
"id=12345 action=walk");
// Exceed the length limit, dropping first element.
stp.clear();
stp.set_action("walk");
aq.full_clear_and_set_limit(3);
LuaAssert(L, stp.from_string("action=walk plane=foo"));
aq.add(12345, stp);
aq.add(12346, stp);
aq.add(12347, stp);
LuaAssert(L, aq.size() == 3);
LuaAssert(L, aq.nth(0).id() == 0);
LuaAssert(L, aq.nth(0).action() == "");
LuaAssert(L, aq.nth(0).bits() == AnimStep::HAS_EVERYTHING);
LuaAssert(L, aq.nth(1).id() == 12346);
LuaAssert(L, aq.nth(2).id() == 12347);
LuaAssert(L, aq.valid());
LuaAssertStrEq(L, aq.debug_string(),
"version=4; limit=3; "
"id=0 action= plane=foo x=0 y=0 z=0 facing=0 graphic=; "
"id=12346 action=walk plane=foo; "
"id=12347 action=walk plane=foo"
);
// Test serialization and deserialization.
aq.set_size_limit(5);
aq.clear_steps();
stp.clear();
stp.set_action("walk");
stp.set_xyz(util::XYZ(3,4,5));
aq.full_clear_and_set_limit(5);
LuaAssert(L, stp.from_string("action=walk x=3 y=4 z=5"));
aq.add(12345, stp);
stp.clear();
stp.set_action("setgraphic");
stp.set_graphic("banana");
LuaAssert(L, stp.from_string("action=setgraphic graphic=banana"));
aq.add(12346, stp);
stp.clear();
stp.set_action("setfacing");
stp.set_facing(301.0);
LuaAssert(L, stp.from_string("action=setfacing facing=301"));
aq.add(12347, stp);
aq.serialize(&sb);
aqds.deserialize(&sb);
LuaAssert(L, aqds.size_limit() == 5);
LuaAssert(L, aqds.size() == 4);
LuaAssert(L, aqds.nth(0).id() == 0);
LuaAssert(L, aqds.nth(0).bits() == AnimStep::HAS_EVERYTHING);
LuaAssert(L, aqds.nth(0).action() == "");
LuaAssert(L, aqds.nth(0).facing() == 0.0);
LuaAssert(L, aqds.nth(0).xyz() == util::XYZ(0,0,0));
LuaAssert(L, aqds.nth(0).graphic() == "");
LuaAssert(L, aqds.nth(0).plane() == "");
LuaAssertStrEq(L, aqds.debug_string(),
"version=4; limit=5; "
"id=0 action= plane= x=0 y=0 z=0 facing=0 graphic=; "
"id=12345 action=walk x=3 y=4 z=5; "
"id=12346 action=setgraphic graphic=banana; "
"id=12347 action=setfacing facing=301"
);
LuaAssert(L, aqds.nth(1).id() == 12345);
LuaAssert(L, aqds.nth(1).bits() == AnimStep::HAS_XYZ);
LuaAssert(L, aqds.nth(1).action() == "walk");
LuaAssert(L, aqds.nth(1).facing() == 0.0);
LuaAssert(L, aqds.nth(1).xyz() == util::XYZ(3,4,5));
LuaAssert(L, aqds.nth(1).graphic() == "");
LuaAssert(L, aqds.nth(1).plane() == "");
LuaAssert(L, aqds.nth(2).id() == 12346);
LuaAssert(L, aqds.nth(2).bits() == AnimStep::HAS_GRAPHIC);
LuaAssert(L, aqds.nth(2).action() == "setgraphic");
LuaAssert(L, aqds.nth(2).facing() == 0.0);
LuaAssert(L, aqds.nth(2).xyz() == util::XYZ(3,4,5));
LuaAssert(L, aqds.nth(2).graphic() == "banana");
LuaAssert(L, aqds.nth(2).plane() == "");
LuaAssert(L, aqds.nth(3).id() == 12347);
LuaAssert(L, aqds.nth(3).bits() == AnimStep::HAS_FACING);
LuaAssert(L, aqds.nth(3).action() == "setfacing");
LuaAssert(L, aqds.nth(3).facing() == 301.0);
LuaAssert(L, aqds.nth(3).xyz() == util::XYZ(3,4,5));
LuaAssert(L, aqds.nth(3).graphic() == "banana");
LuaAssert(L, aqds.nth(3).plane() == "");
// Test difference transmission
// Start with an anim queue with an initial state and a single action.
aq.set_size_limit(10);
aqds.set_size_limit(10);
aq.clear_steps();
aqds.clear_steps();
stp.clear();
stp.set_action("walk");
stp.set_xyz(util::XYZ(3,4,5));
aq.full_clear_and_set_limit(10);
aqds.full_clear_and_set_limit(10);
// Add a single action to the queue and DT
LuaAssert(L, stp.from_string("action=walk x=3 y=4 z=5"));
aq.add(12345, stp);
sb.clear();
LuaAssert(L, aqds.make_patch(aq, &sb));
aqds.apply_patch(&sb);
LuaAssert(L, aqds.size_and_steps_equal(aq));
// Add another action.
stp.clear();
stp.set_action("fnord");
stp.set_facing(123);
stp.set_plane("where");
// Add another action and DT
LuaAssert(L, stp.from_string("action=fnord plane=where facing=123"));
aq.add(232, stp);
// Generate diffs, but add 4 extra bytes.
sb.clear();
LuaAssert(L, aqds.make_patch(aq, &sb));
sb.write_uint32(0);
// Apply the diffs, 4 extra bytes should remain.
aqds.apply_patch(&sb);
LuaAssert(L, sb.total_writes() - sb.total_reads() == 4);
LuaAssert(L, aqds.size_and_steps_equal(aq));
// Change the queue size limit.
aq.set_size_limit(13);
// Generate diffs, but add 4 extra bytes.
aq.set_limit(13);
sb.clear();
LuaAssert(L, !aqds.size_and_steps_equal(aq));
LuaAssert(L, aqds.make_patch(aq, &sb));
@@ -639,7 +660,7 @@ LuaDefine(unittests_animqueue, "c") {
LuaAssert(L, aqds.size_and_steps_equal(aq));
// Discard all but the last action.
aq.set_size_limit(1);
aq.set_limit(1);
sb.clear();
LuaAssert(L, aqds.make_patch(aq, &sb));

View File

@@ -49,6 +49,7 @@
#include <string>
#include <deque>
#include <cassert>
#include <ostream>
#include <unordered_map>
#include "streambuffer.hpp"
#include "util.hpp"
@@ -155,8 +156,15 @@ public:
// Verify that this step echoes the previous step.
bool echoes(const AnimStep &prev) const;
// Convert to a string for debugging purposes.
std::string debug_string() const;
// Convert a string to an animstep (for testing only).
bool from_string(const std::string &s);
};
class AnimQueue {
private:
util::WorldType world_type_;
@@ -174,6 +182,7 @@ public:
const AnimStep &nth(int n) const { return steps_[n]; }
size_t size() const { return steps_.size(); }
int32_t size_limit() const { return size_limit_; }
int64_t version_number() const { return version_number_; }
// Return true if the size limit and steps are identical.
bool size_and_steps_equal(const AnimQueue &aq) const;
@@ -199,10 +208,14 @@ public:
const AnimStep &back() const;
// (For testing): change the size limit.
void set_size_limit(int32_t n);
void full_clear_and_set_limit(int szl);
void set_limit(int szl);
// (For testing): make sure the invariants are preserved.
bool valid() const;
// Convert to a string for debugging purposes.
std::string debug_string() const;
};
#endif // ANIMQUEUE_HPP

View File

@@ -514,5 +514,5 @@ public:
#define LuaStringify(x) #x
#define LuaAssert(L, x) if (!(x)) { luaL_error((L), "Assert failed: %s (file %s line %d)", LuaStringify(x), __FILE__, __LINE__); }
#define LuaAssertStrEq(L, x, y) { std::string _s1_ = (x); std::string _s2_ = (y); if (_s1_ != _s2_) luaL_error((L), "Assert failed: value=%s (file %s line %d)", _s1_.c_str(), __FILE__, __LINE__); }
#endif // LUASTACK_HPP

View File

@@ -85,7 +85,7 @@ void TextGame::do_view_command(const StringVec &cmd) {
for (int64_t id : world_->get_near(actor_id_, 100, true)) {
const Tangible *tan = world_->tangible_get(id);
const AnimStep &aqback = tan->anim_queue_.back();
std::cerr << id << ": " << aqback.graphic() << " " << aqback.plane() << " " << aqback.xyz() << std::endl;
std::cerr << id << ": " << aqback.graphic() << " " << aqback.plane() << " " << aqback.xyz().debug_string() << std::endl;
}
}

View File

@@ -5,6 +5,7 @@
#include "util.hpp"
#include <sys/types.h>
#include <sys/stat.h>
#include <cmath>
#ifndef WIN32
#include <unistd.h>
#define stat _stat
@@ -12,6 +13,21 @@
namespace util {
stringvec split(const std::string &s, char sep) {
stringvec result;
int start = 0;
for (int i = 0; i < int(s.size()); i++) {
if (s[i] == sep) {
result.push_back(s.substr(start, i-start));
start = i+1;
}
}
if (start < int(s.size())) {
result.push_back(s.substr(start));
}
return result;
}
std::string tolower(std::string input) {
for (int i = 0; i < int(input.size()); i++) {
input[i] = std::tolower(input[i]);
@@ -42,6 +58,16 @@ int64_t strtoint(const std::string &value, int64_t errval) {
}
}
double strtodouble(const std::string &value) {
char *endptr;
double result = strtod(value.c_str(), &endptr);
if (endptr == value.c_str() + value.size()) {
return result;
} else {
return std::nan("");
}
}
std::string ltrim(std::string s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(),
std::not1(std::ptr_fun<int, int>(std::isspace))));
@@ -97,31 +123,10 @@ double distance_squared(double x1, double y1, double x2, double y2) {
return dx*dx + dy*dy;
}
// These hash functions are part of Bob Jenkins' Lookup3.
#define hash_rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
#define hash_mix(a,b,c) { \
a -= c; a ^= hash_rot(c, 4); c += b; \
b -= a; b ^= hash_rot(a, 6); a += c; \
c -= b; c ^= hash_rot(b, 8); b += a; \
a -= c; a ^= hash_rot(c,16); c += b; \
b -= a; b ^= hash_rot(a,19); a += c; \
c -= b; c ^= hash_rot(b, 4); b += a; \
}
#define hash_final(a,b,c) { \
c ^= b; c -= hash_rot(b,14); \
a ^= c; a -= hash_rot(c,11); \
b ^= a; b -= hash_rot(a,25); \
c ^= b; c -= hash_rot(b,16); \
a ^= c; a -= hash_rot(c,4); \
b ^= a; b -= hash_rot(a,14); \
c ^= b; c -= hash_rot(b,24); \
}
std::ostream & operator << (std::ostream &out, const XYZ &xyz) {
out << "(" << xyz.x << "," << xyz.y << "," << xyz.z << ")";
return out;
std::string XYZ::debug_string() const {
std::ostringstream oss;
oss << "(" << x << "," << y << "," << z << ")";
return oss.str();
}
} // namespace util

View File

@@ -22,6 +22,9 @@ using stringvec = std::vector<std::string>;
using stringset = std::set<std::string>;
using HashValue = std::pair<uint64_t, uint64_t>;
// Split a string into multiple strings
stringvec split(const std::string &s, char sep);
// String to lowercase/uppercase
std::string tolower(std::string input);
std::string toupper(std::string input);
@@ -32,6 +35,9 @@ bool validinteger(const std::string &value);
// String to integer. Returns errval if the number is not parseable.
int64_t strtoint(const std::string &value, int64_t errval);
// String to double. Returns NAN if the number is not parseable.
double strtodouble(const std::string &value);
// Trim strings: left end, right end, both ends.
std::string ltrim(std::string s);
std::string rtrim(std::string s);
@@ -49,6 +55,7 @@ std::string get_file_fingerprint(const std::string &path);
// Calculate distance between two points
double distance_squared(double x1, double y1, double x2, double y2);
// An XYZ coordinate, general purpose.
struct XYZ {
float x, y, z;
@@ -56,8 +63,9 @@ struct XYZ {
XYZ(float ix, float iy, float iz) { x=ix; y=iy; z=iz; }
bool operator ==(const XYZ &o) const { return x==o.x && y == o.y && z==o.z; }
bool operator !=(const XYZ &o) const { return x!=o.x || y != o.y || z!=o.z; }
std::string debug_string() const;
};
std::ostream & operator << (std::ostream &out, const XYZ &xyz);
} // namespace util
#endif // UTIL_HPP