mirror of
https://github.com/esp8266/Arduino.git
synced 2025-06-13 13:01:55 +03:00
host emulation: improve udp, persistent spiffs (#5605)
This commit is contained in:
@ -17,7 +17,7 @@
|
||||
|
||||
upload the contents of the data folder with MkSPIFFS Tool ("ESP8266 Sketch Data Upload" in Tools menu in Arduino IDE)
|
||||
or you can upload the contents of a folder if you CD in that folder and run the following command:
|
||||
for file in `ls -A1`; do curl -F "file=@$PWD/$file" esp8266fs.local/edit; done
|
||||
for file in `\ls -A1`; do curl -F "file=@$PWD/$file" esp8266fs.local/edit; done
|
||||
|
||||
access the sample web page at http://esp8266fs.local
|
||||
edit the page by going to http://esp8266fs.local/edit
|
||||
|
@ -19,8 +19,8 @@ LCOV ?= lcov
|
||||
GENHTML ?= genhtml
|
||||
|
||||
ifeq ($(FORCE32),1)
|
||||
ABILITY32 = $(shell echo 'int main(){return sizeof(long);}'|$(CXX) -m32 -x c++ - -o sizeoflong 2>/dev/null && ./sizeoflong; echo $$?; rm -f sizeoflong;)
|
||||
ifneq ($(ABILITY32),4)
|
||||
SIZEOFLONG = $(shell echo 'int main(){return sizeof(long);}'|$(CXX) -m32 -x c++ - -o sizeoflong 2>/dev/null && ./sizeoflong; echo $$?; rm -f sizeoflong;)
|
||||
ifneq ($(SIZEOFLONG),4)
|
||||
$(warning Cannot compile in 32 bit mode, switching to native mode)
|
||||
else
|
||||
N32 = 32
|
||||
@ -82,6 +82,8 @@ MOCK_CPP_FILES := $(MOCK_CPP_FILES_COMMON) $(addprefix common/,\
|
||||
|
||||
MOCK_CPP_FILES_EMU := $(MOCK_CPP_FILES_COMMON) $(addprefix common/,\
|
||||
ArduinoMain.cpp \
|
||||
ArduinoMainUdp.cpp \
|
||||
ArduinoMainSpiffs.cpp \
|
||||
user_interface.cpp \
|
||||
)
|
||||
|
||||
|
@ -32,37 +32,13 @@
|
||||
#include <Arduino.h>
|
||||
#include <user_interface.h> // wifi_get_ip_info()
|
||||
|
||||
#include <functional>
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/igmp.h"
|
||||
#include "lwip/mem.h"
|
||||
#include <include/UdpContext.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <unistd.h> // usleep
|
||||
#include <getopt.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#if 0
|
||||
#include "../common/spiffs_mock.h"
|
||||
#include <spiffs/spiffs.h>
|
||||
SPIFFS_MOCK_DECLARE(/*size_kb*/1024, /(blovk_kb*/8, /*page_b*/512);
|
||||
#endif
|
||||
|
||||
std::map<int,UdpContext*> udps;
|
||||
|
||||
void register_udp (int sock, UdpContext* udp)
|
||||
{
|
||||
if (udp)
|
||||
udps[sock] = udp;
|
||||
else
|
||||
udps.erase(sock);
|
||||
}
|
||||
|
||||
bool user_exit = false;
|
||||
const char* host_interface = nullptr;
|
||||
size_t spiffs_kb = 1024;
|
||||
|
||||
void help (const char* argv0, int exitcode)
|
||||
{
|
||||
@ -73,7 +49,9 @@ void help (const char* argv0, int exitcode)
|
||||
" -i <interface> - use this interface for IP address\n"
|
||||
" -l - bind tcp/udp servers to interface only (not 0.0.0.0)\n"
|
||||
" -f - no throttle (possibly 100%%CPU)\n"
|
||||
, argv0);
|
||||
" -S - spiffs size in KBytes (default: %zd)\n"
|
||||
" (negative value will force mismatched size)\n"
|
||||
, argv0, spiffs_kb);
|
||||
exit(exitcode);
|
||||
}
|
||||
|
||||
@ -83,15 +61,36 @@ static struct option options[] =
|
||||
{ "fast", no_argument, NULL, 'f' },
|
||||
{ "local", no_argument, NULL, 'l' },
|
||||
{ "interface", required_argument, NULL, 'i' },
|
||||
{ "spiffskb", required_argument, NULL, 'S' },
|
||||
};
|
||||
|
||||
void save ()
|
||||
{
|
||||
mock_stop_spiffs();
|
||||
}
|
||||
|
||||
void control_c (int sig)
|
||||
{
|
||||
(void)sig;
|
||||
|
||||
if (user_exit)
|
||||
{
|
||||
fprintf(stderr, MOCK "stuck, killing\n");
|
||||
save();
|
||||
exit(1);
|
||||
}
|
||||
user_exit = true;
|
||||
}
|
||||
|
||||
int main (int argc, char* const argv [])
|
||||
{
|
||||
signal(SIGINT, control_c);
|
||||
|
||||
bool fast = false;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int n = getopt_long(argc, argv, "hlfi:", options, NULL);
|
||||
int n = getopt_long(argc, argv, "hlfi:S:", options, NULL);
|
||||
if (n < 0)
|
||||
break;
|
||||
switch (n)
|
||||
@ -108,36 +107,37 @@ int main (int argc, char* const argv [])
|
||||
case 'f':
|
||||
fast = true;
|
||||
break;
|
||||
case 'S':
|
||||
spiffs_kb = atoi(optarg);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, MOCK "bad option '%c'\n", n);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (spiffs_kb)
|
||||
{
|
||||
String name = argv[0];
|
||||
name += "-spiffs";
|
||||
name += String(spiffs_kb > 0? spiffs_kb: -spiffs_kb, DEC);
|
||||
name += "KB";
|
||||
mock_start_spiffs(name, spiffs_kb);
|
||||
}
|
||||
|
||||
// setup global global_ipv4_netfmt
|
||||
wifi_get_ip_info(0, nullptr);
|
||||
|
||||
setup();
|
||||
while (true)
|
||||
while (!user_exit)
|
||||
{
|
||||
if (!fast)
|
||||
usleep(10000); // not 100% cpu
|
||||
|
||||
loop();
|
||||
check_incoming_udp();
|
||||
}
|
||||
|
||||
save();
|
||||
|
||||
// check incoming udp
|
||||
for (auto& udp: udps)
|
||||
{
|
||||
pollfd p;
|
||||
p.fd = udp.first;
|
||||
p.events = POLLIN;
|
||||
if (poll(&p, 1, 0) && p.revents == POLLIN)
|
||||
{
|
||||
fprintf(stderr, MOCK "UDP poll(%d) -> cb\r", p.fd);
|
||||
udp.second->mock_cb();
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
17
tests/host/common/ArduinoMainSpiffs.cpp
Normal file
17
tests/host/common/ArduinoMainSpiffs.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
#include "spiffs_mock.h"
|
||||
|
||||
SpiffsMock* spiffs_mock = nullptr;
|
||||
|
||||
void mock_start_spiffs (const String& fname, size_t size_kb, size_t block_kb, size_t page_b)
|
||||
{
|
||||
spiffs_mock = new SpiffsMock(size_kb * 1024, block_kb * 1024, page_b, fname);
|
||||
}
|
||||
|
||||
void mock_stop_spiffs ()
|
||||
{
|
||||
if (spiffs_mock)
|
||||
delete spiffs_mock;
|
||||
spiffs_mock = nullptr;
|
||||
}
|
||||
|
65
tests/host/common/ArduinoMainUdp.cpp
Normal file
65
tests/host/common/ArduinoMainUdp.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
Arduino emulator main loop
|
||||
Copyright (c) 2018 david gauchard. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal with the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimers.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimers in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- The names of its contributors may not be used to endorse or promote
|
||||
products derived from this Software without specific prior written
|
||||
permission.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS WITH THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/igmp.h"
|
||||
#include "lwip/mem.h"
|
||||
#include <include/UdpContext.h>
|
||||
#include <poll.h>
|
||||
#include <map>
|
||||
|
||||
std::map<int,UdpContext*> udps;
|
||||
|
||||
void register_udp (int sock, UdpContext* udp)
|
||||
{
|
||||
if (udp)
|
||||
udps[sock] = udp;
|
||||
else
|
||||
udps.erase(sock);
|
||||
}
|
||||
|
||||
void check_incoming_udp ()
|
||||
{
|
||||
// check incoming udp
|
||||
for (auto& udp: udps)
|
||||
{
|
||||
pollfd p;
|
||||
p.fd = udp.first;
|
||||
p.events = POLLIN;
|
||||
if (poll(&p, 1, 0) && p.revents == POLLIN)
|
||||
{
|
||||
fprintf(stderr, MOCK "UDP poll(%d) -> cb\r", p.fd);
|
||||
udp.second->mock_cb();
|
||||
}
|
||||
}
|
||||
}
|
@ -66,20 +66,24 @@ int mockConnect (uint32_t ipv4, int& sock, int port)
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t mockFillInBuf (int sock, char* ccinbuf, size_t& ccinbufsize)
|
||||
ssize_t mockFillInBuf (int sock, char* ccinbuf, size_t& ccinbufsize)
|
||||
{
|
||||
size_t maxread = CCBUFSIZE - ccinbufsize;
|
||||
ssize_t ret = ::read(sock, ccinbuf + ccinbufsize, maxread);
|
||||
if (ret == -1)
|
||||
{
|
||||
if (errno != EAGAIN)
|
||||
fprintf(stderr, MOCK "ClientContext::(read/peek): filling buffer for %zd bytes: %s\n", maxread, strerror(errno));
|
||||
{
|
||||
fprintf(stderr, MOCK "ClientContext::(read/peek fd=%i): filling buffer for %zd bytes: %s\n", sock, maxread, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
return ccinbufsize += ret;
|
||||
ccinbufsize += ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t mockPeekBytes (int sock, char* dst, size_t usersize, int timeout_ms, char* ccinbuf, size_t& ccinbufsize)
|
||||
ssize_t mockPeekBytes (int sock, char* dst, size_t usersize, int timeout_ms, char* ccinbuf, size_t& ccinbufsize)
|
||||
{
|
||||
if (usersize > CCBUFSIZE)
|
||||
fprintf(stderr, MOCK "CCBUFSIZE(%d) should be increased by %zd bytes (-> %zd)\n", CCBUFSIZE, usersize - CCBUFSIZE, usersize);
|
||||
@ -96,7 +100,8 @@ size_t mockPeekBytes (int sock, char* dst, size_t usersize, int timeout_ms, char
|
||||
}
|
||||
|
||||
// check incoming data data
|
||||
mockFillInBuf(sock, ccinbuf, ccinbufsize);
|
||||
if (mockFillInBuf(sock, ccinbuf, ccinbufsize) < 0)
|
||||
return -1;
|
||||
if (usersize <= ccinbufsize)
|
||||
{
|
||||
// data just received
|
||||
@ -113,16 +118,18 @@ size_t mockPeekBytes (int sock, char* dst, size_t usersize, int timeout_ms, char
|
||||
return retsize;
|
||||
}
|
||||
|
||||
size_t mockRead (int sock, char* dst, size_t size, int timeout_ms, char* ccinbuf, size_t& ccinbufsize)
|
||||
ssize_t mockRead (int sock, char* dst, size_t size, int timeout_ms, char* ccinbuf, size_t& ccinbufsize)
|
||||
{
|
||||
size_t copied = mockPeekBytes(sock, dst, size, timeout_ms, ccinbuf, ccinbufsize);
|
||||
ssize_t copied = mockPeekBytes(sock, dst, size, timeout_ms, ccinbuf, ccinbufsize);
|
||||
if (copied < 0)
|
||||
return -1;
|
||||
// swallow (XXX use a circular buffer)
|
||||
memmove(ccinbuf, ccinbuf + copied, ccinbufsize - copied);
|
||||
ccinbufsize -= copied;
|
||||
return copied;
|
||||
}
|
||||
|
||||
size_t mockWrite (int sock, const uint8_t* data, size_t size, int timeout_ms)
|
||||
ssize_t mockWrite (int sock, const uint8_t* data, size_t size, int timeout_ms)
|
||||
{
|
||||
struct pollfd p;
|
||||
p.fd = sock;
|
||||
@ -140,7 +147,7 @@ size_t mockWrite (int sock, const uint8_t* data, size_t size, int timeout_ms)
|
||||
if (ret == -1)
|
||||
{
|
||||
fprintf(stderr, MOCK "ClientContext::read: write(%d): %s\n", sock, strerror(errno));
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
if (ret != (int)size)
|
||||
{
|
||||
|
@ -40,6 +40,7 @@ HardwareSerial::HardwareSerial (int uart_nr)
|
||||
{
|
||||
if (uart_nr != 0)
|
||||
fprintf(stderr, MOCK "FIXME HardwareSerial::HardwareSerial(%d)\n", uart_nr);
|
||||
_uart = (decltype(_uart))1; // not used, for 'while (!Serial);' to pass
|
||||
}
|
||||
|
||||
void HardwareSerial::begin (unsigned long baud, SerialConfig config, SerialMode mode, uint8_t tx_pin)
|
||||
|
@ -150,12 +150,17 @@ size_t mockUDPPeekBytes (int sock, char* dst, size_t usersize, int timeout_ms, c
|
||||
return retsize;
|
||||
}
|
||||
|
||||
void mockUDPSwallow (size_t copied, char* ccinbuf, size_t& ccinbufsize)
|
||||
{
|
||||
// poor man buffer
|
||||
memmove(ccinbuf, ccinbuf + copied, ccinbufsize - copied);
|
||||
ccinbufsize -= copied;
|
||||
}
|
||||
|
||||
size_t mockUDPRead (int sock, char* dst, size_t size, int timeout_ms, char* ccinbuf, size_t& ccinbufsize)
|
||||
{
|
||||
size_t copied = mockUDPPeekBytes(sock, dst, size, timeout_ms, ccinbuf, ccinbufsize);
|
||||
// swallow (XXX use a circular buffer?)
|
||||
memmove(ccinbuf, ccinbuf + copied, ccinbufsize - copied);
|
||||
ccinbufsize -= copied;
|
||||
mockUDPSwallow(copied, ccinbuf, ccinbufsize);
|
||||
return copied;
|
||||
}
|
||||
|
||||
|
@ -3,5 +3,7 @@
|
||||
#define FAKE_ESP8266_PERI_H
|
||||
|
||||
const int GPI = 0;
|
||||
const int GPO = 0;
|
||||
const int GP16I = 0;
|
||||
|
||||
#endif
|
@ -156,18 +156,28 @@ public:
|
||||
|
||||
size_t getSize()
|
||||
{
|
||||
return _inbufsize?: mockFillInBuf(_sock, _inbuf, _inbufsize);
|
||||
if (_sock < 0)
|
||||
return 0;
|
||||
if (_inbufsize)
|
||||
return _inbufsize;
|
||||
return mockFillInBuf(_sock, _inbuf, _inbufsize);
|
||||
}
|
||||
|
||||
int read()
|
||||
{
|
||||
char c;
|
||||
return read(&c, 1)? c: -1;
|
||||
return read(&c, 1)? (unsigned char)c: -1;
|
||||
}
|
||||
|
||||
size_t read (char* dst, size_t size)
|
||||
{
|
||||
return mockRead(_sock, dst, size, 0, _inbuf, _inbufsize);
|
||||
ssize_t ret = mockRead(_sock, dst, size, 0, _inbuf, _inbufsize);
|
||||
if (ret < 0)
|
||||
{
|
||||
abort(); // close, CLOSED
|
||||
return 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int peek()
|
||||
@ -198,7 +208,13 @@ public:
|
||||
|
||||
size_t write(const uint8_t* data, size_t size)
|
||||
{
|
||||
return mockWrite(_sock, data, size, _timeout_ms);
|
||||
ssize_t ret = mockWrite(_sock, data, size, _timeout_ms);
|
||||
if (ret < 0)
|
||||
{
|
||||
abort(); // close, CLOSED
|
||||
return 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t write(Stream& stream)
|
||||
@ -208,7 +224,7 @@ public:
|
||||
avail = stream.readBytes(buf, avail);
|
||||
size_t totwrote = 0;
|
||||
uint8_t* w = buf;
|
||||
while (avail)
|
||||
while (avail && _sock >= 0)
|
||||
{
|
||||
size_t wrote = write(w, avail);
|
||||
w += wrote;
|
||||
|
@ -79,20 +79,12 @@ public:
|
||||
_sock = -1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void setMulticastInterface(const ip_addr_t& addr)
|
||||
{
|
||||
(void)addr;
|
||||
// user multicast, and this is how it works with posix: send to multicast address:
|
||||
_dst.addr = staticMCastAddr;
|
||||
}
|
||||
#endif
|
||||
void setMulticastInterface(const ip_addr_t* addr)
|
||||
{
|
||||
(void)addr;
|
||||
// user multicast, and this is how it works with posix: send to multicast address:
|
||||
_dst.addr = staticMCastAddr;
|
||||
}
|
||||
|
||||
void setMulticastTTL(int ttl)
|
||||
{
|
||||
@ -118,12 +110,12 @@ public:
|
||||
|
||||
void seek(const size_t pos)
|
||||
{
|
||||
fprintf(stderr, MOCK "TODO: implement UDP offset\n");
|
||||
if (!isValidOffset(pos))
|
||||
{
|
||||
fprintf(stderr, MOCK "UDPContext::seek too far (%zd >= %zd)\n", pos, _inbufsize);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
mockUDPSwallow(pos, _inbuf, _inbufsize);
|
||||
}
|
||||
|
||||
bool isValidOffset(const size_t pos) const {
|
||||
|
@ -91,24 +91,26 @@ extern uint32_t global_ipv4_netfmt; // selected interface addresse to bind to
|
||||
#ifdef __cplusplus
|
||||
|
||||
#ifndef CCBUFSIZE
|
||||
#define CCBUFSIZE 8192
|
||||
#define CCBUFSIZE 65536
|
||||
#endif
|
||||
|
||||
// tcp
|
||||
int mockConnect (uint32_t addr, int& sock, int port);
|
||||
size_t mockFillInBuf (int sock, char* ccinbuf, size_t& ccinbufsize);
|
||||
size_t mockPeekBytes (int sock, char* dst, size_t size, int timeout_ms, char* buf, size_t& bufsize);
|
||||
size_t mockRead (int sock, char* dst, size_t size, int timeout_ms, char* buf, size_t& bufsize);
|
||||
size_t mockWrite (int sock, const uint8_t* data, size_t size, int timeout_ms);
|
||||
ssize_t mockFillInBuf (int sock, char* ccinbuf, size_t& ccinbufsize);
|
||||
ssize_t mockPeekBytes (int sock, char* dst, size_t size, int timeout_ms, char* buf, size_t& bufsize);
|
||||
ssize_t mockRead (int sock, char* dst, size_t size, int timeout_ms, char* buf, size_t& bufsize);
|
||||
ssize_t mockWrite (int sock, const uint8_t* data, size_t size, int timeout_ms);
|
||||
int serverAccept (int sock);
|
||||
|
||||
// udp
|
||||
void check_incoming_udp ();
|
||||
int mockUDPSocket ();
|
||||
bool mockUDPListen (int sock, uint32_t dstaddr, uint16_t port, uint32_t mcast = 0);
|
||||
size_t mockUDPFillInBuf (int sock, char* ccinbuf, size_t& ccinbufsize, uint8_t& addrsize, uint8_t addr[16], uint16_t& port);
|
||||
size_t mockUDPPeekBytes (int sock, char* dst, size_t usersize, int timeout_ms, char* ccinbuf, size_t& ccinbufsize);
|
||||
size_t mockUDPRead (int sock, char* dst, size_t size, int timeout_ms, char* ccinbuf, size_t& ccinbufsize);
|
||||
size_t mockUDPWrite (int sock, const uint8_t* data, size_t size, int timeout_ms, uint32_t ipv4, uint16_t port);
|
||||
void mockUDPSwallow (size_t copied, char* ccinbuf, size_t& ccinbufsize);
|
||||
|
||||
class UdpContext;
|
||||
void register_udp (int sock, UdpContext* udp = nullptr);
|
||||
@ -117,6 +119,11 @@ class InterruptLock { };
|
||||
|
||||
//
|
||||
|
||||
void mock_start_spiffs (const String& fname, size_t size_kb, size_t block_kb = 8, size_t page_b = 512);
|
||||
void mock_stop_spiffs ();
|
||||
|
||||
//
|
||||
|
||||
#define CORE_MOCK 1
|
||||
|
||||
#define ARDUINO 267
|
||||
@ -142,4 +149,8 @@ class InterruptLock { };
|
||||
|
||||
//
|
||||
|
||||
#include <common/esp8266_peri.h>
|
||||
|
||||
//
|
||||
|
||||
#endif // __cplusplus
|
||||
|
@ -27,8 +27,6 @@
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define SPIFFS_FILE_NAME "spiffs.bin"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
static uint32_t s_phys_addr = 0;
|
||||
@ -40,87 +38,98 @@ extern "C"
|
||||
|
||||
FS SPIFFS(nullptr);
|
||||
|
||||
SpiffsMock::SpiffsMock(size_t fs_size, size_t fs_block, size_t fs_page, bool storage)
|
||||
SpiffsMock::SpiffsMock(ssize_t fs_size, size_t fs_block, size_t fs_page, const String& storage)
|
||||
{
|
||||
m_storage = storage;
|
||||
if ((m_overwrite = (fs_size < 0)))
|
||||
fs_size = -fs_size;
|
||||
|
||||
fprintf(stderr, "SPIFFS: %zd bytes\n", fs_size);
|
||||
|
||||
m_storage = storage;
|
||||
m_fs = new uint8_t[m_fs_size = fs_size];
|
||||
memset(&m_fs[0], 0xff, m_fs_size);
|
||||
|
||||
m_fs.resize(fs_size, 0xff);
|
||||
s_phys_addr = 0;
|
||||
s_phys_size = static_cast<uint32_t>(fs_size);
|
||||
s_phys_page = static_cast<uint32_t>(fs_page);
|
||||
s_phys_block = static_cast<uint32_t>(fs_block);
|
||||
s_phys_data = &m_fs[0];
|
||||
s_phys_data = m_fs.data();
|
||||
reset();
|
||||
}
|
||||
|
||||
void SpiffsMock::reset()
|
||||
{
|
||||
SPIFFS = FS(FSImplPtr(new SPIFFSImpl(0, s_phys_size, s_phys_page, s_phys_block, 5)));
|
||||
if (m_storage)
|
||||
load();
|
||||
}
|
||||
|
||||
SpiffsMock::~SpiffsMock()
|
||||
{
|
||||
if (m_storage)
|
||||
save();
|
||||
s_phys_addr = 0;
|
||||
s_phys_size = 0;
|
||||
s_phys_page = 0;
|
||||
s_phys_block = 0;
|
||||
s_phys_data = nullptr;
|
||||
delete [] m_fs;
|
||||
m_fs = nullptr;
|
||||
m_fs_size = 0;
|
||||
m_fs.resize(0);
|
||||
SPIFFS = FS(FSImplPtr(nullptr));
|
||||
}
|
||||
|
||||
void SpiffsMock::load ()
|
||||
{
|
||||
if (!m_fs_size)
|
||||
if (!m_fs.size() || !m_storage.length())
|
||||
return;
|
||||
|
||||
const char* fname = getenv("SPIFFS_PATH");
|
||||
if (!fname)
|
||||
fname = DEFAULT_SPIFFS_FILE_NAME;
|
||||
int fs = ::open(SPIFFS_FILE_NAME, O_RDONLY);
|
||||
int fs = ::open(m_storage.c_str(), O_RDONLY);
|
||||
if (fs == -1)
|
||||
{
|
||||
fprintf(stderr, "SPIFFS: loading '%s': %s\n", fname, strerror(errno));
|
||||
fprintf(stderr, "SPIFFS: loading '%s': %s\n", m_storage.c_str(), strerror(errno));
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "SPIFFS: loading %zi bytes from '%s'\n", m_fs_size, fname);
|
||||
if (::read(fs, &m_fs[0], m_fs_size) != (ssize_t)m_fs_size)
|
||||
fprintf(stderr, "SPIFFS: reading %zi bytes: %s\n", m_fs_size, strerror(errno));
|
||||
|
||||
off_t flen = lseek(fs, 0, SEEK_END);
|
||||
if (flen == (off_t)-1)
|
||||
{
|
||||
fprintf(stderr, "SPIFFS: checking size of '%s': %s\n", m_storage.c_str(), strerror(errno));
|
||||
return;
|
||||
}
|
||||
lseek(fs, 0, SEEK_SET);
|
||||
|
||||
if (flen != (off_t)m_fs.size())
|
||||
{
|
||||
fprintf(stderr, "SPIFFS: size of '%s': %d does not match requested size %zd\n", m_storage.c_str(), (int)flen, m_fs.size());
|
||||
if (!m_overwrite)
|
||||
{
|
||||
fprintf(stderr, "SPIFFS: aborting at user request\n");
|
||||
exit(1);
|
||||
}
|
||||
fprintf(stderr, "SPIFFS: continuing without loading at user request, '%s' will be overwritten\n", m_storage.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "SPIFFS: loading %zi bytes from '%s'\n", m_fs.size(), m_storage.c_str());
|
||||
ssize_t r = ::read(fs, m_fs.data(), m_fs.size());
|
||||
if (r != (ssize_t)m_fs.size())
|
||||
fprintf(stderr, "SPIFFS: reading %zi bytes: returned %zd: %s\n", m_fs.size(), r, strerror(errno));
|
||||
}
|
||||
::close(fs);
|
||||
}
|
||||
|
||||
void SpiffsMock::save ()
|
||||
{
|
||||
if (!m_fs_size)
|
||||
if (!m_fs.size() || !m_storage.length())
|
||||
return;
|
||||
|
||||
const char* fname = getenv("SPIFFS_PATH");
|
||||
if (!fname)
|
||||
fname = DEFAULT_SPIFFS_FILE_NAME;
|
||||
int fs = ::open(SPIFFS_FILE_NAME, O_CREAT | O_TRUNC | O_WRONLY, 0644);
|
||||
int fs = ::open(m_storage.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
|
||||
if (fs == -1)
|
||||
{
|
||||
fprintf(stderr, "SPIFFS: saving '%s': %s\n", fname, strerror(errno));
|
||||
fprintf(stderr, "SPIFFS: saving '%s': %s\n", m_storage.c_str(), strerror(errno));
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "SPIFFS: saving %zi bytes to '%s'\n", m_fs_size, fname);
|
||||
fprintf(stderr, "SPIFFS: saving %zi bytes to '%s'\n", m_fs.size(), m_storage.c_str());
|
||||
|
||||
// this can be a valgrind error, I don't understand how it happens
|
||||
//for (size_t i = 0; i < m_fs_size; i++) printf("\r%zd:%d ", i, (int)m_fs[i]);
|
||||
|
||||
if (::write(fs, &m_fs[0], m_fs_size) != (ssize_t)m_fs_size)
|
||||
fprintf(stderr, "SPIFFS: writing %zi bytes: %s\n", m_fs_size, strerror(errno));
|
||||
if (::write(fs, m_fs.data(), m_fs.size()) != (ssize_t)m_fs.size())
|
||||
fprintf(stderr, "SPIFFS: writing %zi bytes: %s\n", m_fs.size(), strerror(errno));
|
||||
if (::close(fs) == -1)
|
||||
fprintf(stderr, "SPIFFS: closing %s: %s\n", fname, strerror(errno));
|
||||
fprintf(stderr, "SPIFFS: closing %s: %s\n", m_storage.c_str(), strerror(errno));
|
||||
}
|
||||
|
||||
int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) {
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
class SpiffsMock {
|
||||
public:
|
||||
SpiffsMock(size_t fs_size, size_t fs_block, size_t fs_page, bool storage = true);
|
||||
SpiffsMock(ssize_t fs_size, size_t fs_block, size_t fs_page, const String& storage = emptyString);
|
||||
void reset();
|
||||
~SpiffsMock();
|
||||
|
||||
@ -33,21 +33,12 @@ protected:
|
||||
void load ();
|
||||
void save ();
|
||||
|
||||
// it was a vector, but CI tests & valgrind complain with:
|
||||
// Syscall param write(buf) points to uninitialised byte(s)
|
||||
// by 0x43E9FF: SpiffsMock::save() (spiffs_mock.cpp:116)
|
||||
// = if (::write(fs, &m_fs[0], m_fs_size) != (ssize_t)m_fs_size)
|
||||
// so switched to a regular array
|
||||
// and that bug is still here
|
||||
// XXXWIPTODO
|
||||
|
||||
uint8_t* m_fs;
|
||||
size_t m_fs_size;
|
||||
bool m_storage;
|
||||
std::vector<uint8_t> m_fs;
|
||||
String m_storage;
|
||||
bool m_overwrite;
|
||||
};
|
||||
|
||||
#define SPIFFS_MOCK_DECLARE(size_kb, block_kb, page_b, storage) SpiffsMock spiffs_mock(size_kb * 1024, block_kb * 1024, page_b, storage)
|
||||
#define SPIFFS_MOCK_RESET() spiffs_mock.reset()
|
||||
|
||||
|
||||
#endif /* spiffs_mock_hpp */
|
||||
|
@ -50,25 +50,25 @@ static std::set<String> listDir (const char* path)
|
||||
|
||||
TEST_CASE("FS can begin","[fs]")
|
||||
{
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, false);
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, "");
|
||||
REQUIRE(SPIFFS.begin());
|
||||
}
|
||||
|
||||
TEST_CASE("FS can't begin with zero size","[fs]")
|
||||
{
|
||||
SPIFFS_MOCK_DECLARE(0, 8, 512, false);
|
||||
SPIFFS_MOCK_DECLARE(0, 8, 512, "");
|
||||
REQUIRE_FALSE(SPIFFS.begin());
|
||||
}
|
||||
|
||||
TEST_CASE("Before begin is called, open will fail","[fs]")
|
||||
{
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, false);
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, "");
|
||||
REQUIRE_FALSE(SPIFFS.open("/foo", "w"));
|
||||
}
|
||||
|
||||
TEST_CASE("FS can create file","[fs]")
|
||||
{
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, false);
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, "");
|
||||
REQUIRE(SPIFFS.begin());
|
||||
createFile("/test", "");
|
||||
REQUIRE(SPIFFS.exists("/test"));
|
||||
@ -76,7 +76,7 @@ TEST_CASE("FS can create file","[fs]")
|
||||
|
||||
TEST_CASE("Files can be written and appended to","[fs]")
|
||||
{
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, false);
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, "");
|
||||
REQUIRE(SPIFFS.begin());
|
||||
{
|
||||
File f = SPIFFS.open("config1.txt", "w");
|
||||
@ -100,7 +100,7 @@ TEST_CASE("Files can be written and appended to","[fs]")
|
||||
|
||||
TEST_CASE("Files persist after reset", "[fs]")
|
||||
{
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, false);
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, "");
|
||||
REQUIRE(SPIFFS.begin());
|
||||
createFile("config1.txt", "file 1");
|
||||
|
||||
@ -112,7 +112,7 @@ TEST_CASE("Files persist after reset", "[fs]")
|
||||
|
||||
TEST_CASE("Filesystem is empty after format", "[fs]")
|
||||
{
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, false);
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, "");
|
||||
REQUIRE(SPIFFS.format());
|
||||
REQUIRE(SPIFFS.begin());
|
||||
createFile("/1", "first");
|
||||
@ -128,7 +128,7 @@ TEST_CASE("Filesystem is empty after format", "[fs]")
|
||||
|
||||
TEST_CASE("Dir lists all files", "[fs]")
|
||||
{
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, false);
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, "");
|
||||
REQUIRE(SPIFFS.begin());
|
||||
createFile("/empty", "");
|
||||
createFile("/not_empty", "some text");
|
||||
@ -146,7 +146,7 @@ TEST_CASE("Dir lists all files", "[fs]")
|
||||
|
||||
TEST_CASE("File names which are too long are rejected", "[fs]")
|
||||
{
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, false);
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, "");
|
||||
REQUIRE(SPIFFS.begin());
|
||||
const char* emptyName = "";
|
||||
const char* longName_31 = "/234567890123456789012345678901";
|
||||
@ -164,7 +164,7 @@ TEST_CASE("File names which are too long are rejected", "[fs]")
|
||||
|
||||
TEST_CASE("#1685 Duplicate files", "[fs][bugreport]")
|
||||
{
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, false);
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, "");
|
||||
REQUIRE(SPIFFS.begin());
|
||||
createFile("/config", "some text");
|
||||
createFile("/data", "");
|
||||
@ -175,7 +175,7 @@ TEST_CASE("#1685 Duplicate files", "[fs][bugreport]")
|
||||
|
||||
TEST_CASE("#1819 Can list all files with openDir(\"\")", "[fs][bugreport]")
|
||||
{
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, false);
|
||||
SPIFFS_MOCK_DECLARE(64, 8, 512, "");
|
||||
REQUIRE(SPIFFS.begin());
|
||||
createFile("/file1", "some text");
|
||||
createFile("/file2", "other text");
|
||||
|
@ -112,6 +112,12 @@ elif [ "$BUILD_TYPE" = "platformio_even" ]; then
|
||||
elif [ "$BUILD_TYPE" = "platformio_odd" ]; then
|
||||
BUILD_PARITY=odd tests/platformio-custom.sh
|
||||
|
||||
elif [ "$BUILD_TYPE" = host ]; then
|
||||
tests/ci/host_test.sh
|
||||
|
||||
elif [ "$BUILD_TYPE" = style ]; then
|
||||
tests/ci/install_astyle.sh
|
||||
|
||||
else
|
||||
echo "BUILD_TYPE not set or invalid"
|
||||
exit 1
|
||||
|
Reference in New Issue
Block a user