#pragma once // Host doubles for every provider, deliberately dumb: they record what a // binding asked for and hand back canned values, so a test asserts the // marshalling rather than a device. #include #include #include #include namespace fake { using namespace esp32lua; struct Log : LogProvider { LogLevel level = LogLevel::Debug; std::string message; void write(LogLevel nextLevel, const std::string& nextMessage) override { level = nextLevel; message = nextMessage; } }; struct Settings : SettingsProvider { int32_t degrees = 0; std::string tz = "UTC0"; int32_t rotation() const override { return degrees; } Status setRotation(int32_t value) override { degrees = value; return Status::success(); } std::string timezone() const override { return tz; } Status setTimezone(const std::string& value) override { tz = value; return Status::success(); } }; struct Sys : SysProvider { int32_t millis() const override { return 1234; } MemoryInfo memory() const override { const MemoryInfo info = {100, 200, 50}; return info; } bool isClockSynced() const override { return true; } }; struct Fs : FsProvider { std::map files; std::string written; // One buffer handed to the runtime, which owns and deletes it. struct Reader : FileReader { std::string content; size_t at = 0; int32_t read(char* out, int32_t maxBytes) override { const size_t remaining = content.size() - at; const size_t count = remaining < static_cast(maxBytes) ? remaining : static_cast(maxBytes); content.copy(out, count, at); at += count; return static_cast(count); } }; FileReader* openRead(const std::string& path) override { const std::map::const_iterator found = files.find(path); if (found == files.end()) return nullptr; Reader* reader = new Reader(); reader->content = found->second; return reader; } bool exists(const std::string& path) const override { return files.count(path) != 0; } Status fileSize(const std::string& path, int32_t& size) const override { const std::map::const_iterator found = files.find(path); if (found == files.end()) return Status::failure("no such file"); size = static_cast(found->second.size()); return Status::success(); } Status listDirs(const std::string&, std::vector& names) const override { names.push_back("apps"); return Status::success(); } Status listFiles(const std::string&, std::vector& names) const override { names.push_back("main.lua"); return Status::success(); } Status mkdir(const std::string&) override { return Status::success(); } Status readFile(const std::string& path, int32_t maxBytes, std::string& content) const override { const std::map::const_iterator found = files.find(path); if (found == files.end()) return Status::failure("no such file"); if (static_cast(found->second.size()) > maxBytes) return Status::failure("too large"); content = found->second; return Status::success(); } Status readLineAt(const std::string& path, int32_t offset, int32_t, bool& found, std::string& line, int32_t& nextOffset) const override { const std::map::const_iterator file = files.find(path); if (file == files.end()) return Status::failure("no such file"); if (offset >= static_cast(file->second.size())) { found = false; return Status::success(); } const size_t end = file->second.find('\n', offset); line = file->second.substr( offset, end == std::string::npos ? std::string::npos : end - offset); nextOffset = end == std::string::npos ? static_cast(file->second.size()) : static_cast(end + 1); found = true; return Status::success(); } Status remove(const std::string& path) override { files.erase(path); return Status::success(); } Status removeTree(const std::string&) override { return Status::success(); } Status rename(const std::string&, const std::string&) override { return Status::success(); } Status writeFile(const std::string& path, const std::string& content) override { files[path] = content; written = content; return Status::success(); } }; struct Gui : GuiProvider { std::string trace; int32_t degrees = 0; bool fullscreen = false; bool gradient = false; FontIds fonts() const override { const FontIds ids = {1, 2, 3, 4, 0, 1}; return ids; } int32_t width() const override { return 320; } int32_t height() const override { return 240; } int32_t rotation() const override { return degrees; } void setRotation(int32_t value) override { degrees = value; } int32_t color(int32_t r, int32_t g, int32_t b) const override { return (r << 16) | (g << 8) | b; } void clear(int32_t) override { trace += "clear;"; } void fillRect(int32_t, int32_t, int32_t, int32_t, int32_t) override { trace += "fillRect;"; } void drawRect(int32_t, int32_t, int32_t, int32_t, int32_t) override { trace += "drawRect;"; } void drawLine(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t) override { trace += "drawLine;"; } void drawPixel(int32_t, int32_t, int32_t) override { trace += "drawPixel;"; } void drawCircle(int32_t, int32_t, int32_t, int32_t, int32_t) override { trace += "drawCircle;"; } void fillCircle(int32_t, int32_t, int32_t, int32_t, const int32_t*) override { trace += "fillCircle;"; } void roundRect(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, const int32_t* top, const int32_t* bottom, const int32_t* border) override { if (top && bottom && *top != *bottom) gradient = true; trace += "roundRect("; trace += top ? "fill" : "-"; trace += border ? ",border" : ",-"; trace += ");"; } void setFullscreen(bool on) override { fullscreen = on; } // Stands in for an e-ink panel, where a second commit is a second visible // refresh. void commit() override { commits++; } int commits = 0; void fillPolygon(const int32_t*, const int32_t*, size_t count, int32_t) override { trace += "fillPolygon" + std::to_string(count) + ";"; } Status drawBmp(const std::string&, const int32_t*, const int32_t*, const int32_t*, const int32_t*) override { trace += "drawBmp;"; return Status::success(); } int32_t textWidth(int32_t, const std::string& text, int32_t) const override { return static_cast(text.size()) * 8; } int32_t fontHeight(int32_t, int32_t) const override { return 16; } void drawText(int32_t, int32_t, int32_t, const std::string& text, int32_t, int32_t, const int32_t*) override { trace += "drawText(" + text + ");"; } }; struct Http : HttpProvider { std::string method; std::string body; std::vector headers; int32_t limit = 0; Status request(const std::string& nextMethod, const std::string&, const std::string& nextBody, const std::vector& nextHeaders, int32_t maxBytes, HttpResponse& response) override { method = nextMethod; body = nextBody; headers = nextHeaders; limit = maxBytes; response.status = 404; response.body = "missing"; return Status::success(); } Status download(const std::string&, const std::string&, const HttpDownload& options, int32_t& bytesWritten) override { bytesWritten = options.maxBytes; return Status::success(); } }; struct Timer : TimerProvider { std::vector scheduled; std::vector cancelled; Status schedule(TimerId id, int32_t, bool) override { scheduled.push_back(id); return Status::success(); } void cancel(TimerId id) override { cancelled.push_back(id); } }; struct Wifi : WifiProvider { WifiStatus current; bool savedReconnect = false; Wifi() { current.state = "connected"; current.ssid = "home"; current.ip = "192.168.1.5"; current.rssi = -50; } Status scan(std::vector& networks) override { const WifiNetwork network = {"home", -50, true}; networks.push_back(network); return Status::success(); } Status connect(const std::string* ssid, const std::string*) override { savedReconnect = ssid == nullptr; return Status::success(); } WifiStatus status() const override { return current; } void disconnect() override { current.state = "disconnected"; } Status forget() override { return Status::success(); } }; struct Ble : BleProvider { int32_t duration = 0; std::string value; bool initialized = false; Status init(const std::string*) override { initialized = true; return Status::success(); } void deinit() override { initialized = false; } bool isInitialized() const override { return initialized; } Status scan(int32_t durationMs, std::vector& devices) override { duration = durationMs; const BleDevice device = {"tag", "aa:bb", -60}; devices.push_back(device); return Status::success(); } Status connect(const std::string&) override { return Status::success(); } void disconnect() override {} bool isConnected() const override { return true; } Status read(const std::string&, const std::string&, std::string& out) override { out = std::string("a\0b", 3); return Status::success(); } Status write(const std::string&, const std::string&, const std::string& next) override { value = next; return Status::success(); } Status startAdvertising(const std::string*) override { return Status::success(); } void stopAdvertising() override {} }; struct Touch : TouchProvider { bool down = true; int32_t calibration[4] = {0, 0, 0, 0}; bool touch(int32_t& x, int32_t& y) const override { x = 10; y = 20; return down; } bool rawTouch(int32_t& x, int32_t& y) const override { x = 300; y = 700; return down; } bool isTouched() const override { return down; } Status setCalibration(int32_t x0, int32_t y0, int32_t x1, int32_t y1) override { calibration[0] = x0; calibration[1] = y0; calibration[2] = x1; calibration[3] = y1; return Status::success(); } }; struct Buttons : ButtonsProvider { std::vector buttons() const override { std::vector roles; roles.push_back("left"); roles.push_back("right"); roles.push_back("confirm"); roles.push_back("back"); return roles; } bool isAnyPressed() const override { return false; } bool isPressed(const std::string& button) const override { return button == "confirm"; } bool wasPressed(const std::string&) const override { return false; } bool wasReleased(const std::string&) const override { return false; } }; // Every provider a Runtime needs, so a test names only what it asserts on. struct Bench { Log log; Settings settings; Sys sys; Fs fs; Gui gui; Http http; Timer timer; Wifi wifi; Ble ble; Touch touch; Buttons buttons; Providers providers() { Providers providers; providers.log = &log; providers.settings = &settings; providers.sys = &sys; providers.fs = &fs; providers.gui = &gui; providers.http = &http; providers.timer = &timer; providers.wifi = &wifi; providers.ble = &ble; providers.touch = &touch; providers.buttons = &buttons; return providers; } }; } // namespace fake