#include "wrap-string.hpp" #include "drivenengine.hpp" #include "streambuffer.hpp" #include "world.hpp" #include "traceback.hpp" #include static void run_unittests(lua_State *L) { LuaVar unittests, name, func, err, globtab; LuaExtStack LS(L, unittests, name, func, err, globtab); LS.getglobaltable(globtab); LS.rawget(unittests, globtab, "unittests"); // Sort the unit test names. eng::set names; LS.set(name, LuaNil); while (LS.next(unittests, name, func) != 0) { if (LS.isfunction(func) && LS.isstring(name)) { names.insert(LS.ckstring(name)); } } // Run the functions in order bool any = false; for (const eng::string &name : names) { util::dprint("Running unittests: ", name); LS.rawget(func, unittests, name); lua_pushvalue(L, func.index()); eng::string msg = traceback_pcall(L, 0, 0); if (!msg.empty()) { LS.set(err, msg); util::dprint(msg); any = true; } } if (any) { exit(1); } } class RunUnitTests : public DrivenEngine { public: UniqueWorld world_; RunUnitTests(EngineWrapper *) { world_.reset(new World(WORLD_TYPE_MASTER)); rescan_lua_source(true); } virtual void event_access(AccessKind kind, int64_t place_id, std::string_view datapk, StreamBuffer *retpk) override { switch (kind) { case AccessKind::INVOKE_LUA_SOURCE: { world_->update_source(datapk); run_unittests(world_->state()); stop_driver(); break; } default: break; } } void event_update() override {} }; //////////////////////////////////////////////////////////// // // Driver tests. // // These tests can't go through the unit testing framework, // because they're tests of the driver-engine interface // and the driver. // //////////////////////////////////////////////////////////// static void write_closed_message(Channel *ch) { util::dprint("Chan ", ch->chid(), " closed [", ch->error(), "]\n"); } static void dump_lines(StreamBuffer *in, int chid) { while (true) { eng::string l = in->readline(); if (l == "") break; util::dprint("Chan ", chid, ": ", l); } } // This test connects to a public webserver and prints // the output from the server. class DriverWebServerTest : public DrivenEngine { public: eng::vector channels_; DriverWebServerTest(EngineWrapper *) { SharedChannel ch = new_outgoing_channel("cert:stanford.edu:443"); ch->out()->write_bytes("GET https://stanford.edu/xbanankjdsh.html HTTP/1.1\n\n"); channels_.emplace_back(std::move(ch)); } virtual void event_update() override { eng::vector keep; for (SharedChannel &ch : channels_) { dump_lines(ch->in(), ch->chid()); if (ch->closed()) { write_closed_message(ch.get()); } else { keep.emplace_back(std::move(ch)); } } channels_ = std::move(keep); } }; // This test produces a DNS resolution failure. class DriverDNSFailTest : public DrivenEngine { public: eng::vector channels_; DriverDNSFailTest(EngineWrapper *) { SharedChannel ch = new_outgoing_channel("akjsdkajshdakjshd.alk:80"); ch->out()->write_bytes("GET http://stanford.edu/index.html HTTP/1.1\n\n"); channels_.emplace_back(std::move(ch)); } virtual void event_update() override { eng::vector keep; for (SharedChannel &ch : channels_) { dump_lines(ch->in(), ch->chid()); if (ch->closed()) { write_closed_message(ch.get()); } else { keep.emplace_back(std::move(ch)); } } channels_ = std::move(keep); } }; // This test just prints the time. class DriverPrintClockTest : public DrivenEngine { public: int count_; double last_clock_; DriverPrintClockTest(EngineWrapper *) { count_ = 0; last_clock_ = 0.0; } virtual void event_update() override { double clock = get_clock(); if (clock > last_clock_ + 0.5) { int ms = eng::memhash(); util::dprint(std::fixed, std::setprecision(2), clock, " ", std::hex, ms); count_++; last_clock_ = clock; } } }; DrivenEngineDefine("driverwebservertest", DriverWebServerTest); DrivenEngineDefine("driverdnsfailtest", DriverDNSFailTest); DrivenEngineDefine("driverprintclocktest", DriverPrintClockTest); DrivenEngineDefine("rununittests", RunUnitTests);