diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino
index ff4204476f..cf1c3786d9 100644
--- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino
+++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino
@@ -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
diff --git a/tests/host/Makefile b/tests/host/Makefile
index a795dcc68c..5f8285374e 100644
--- a/tests/host/Makefile
+++ b/tests/host/Makefile
@@ -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 \
 )
 
diff --git a/tests/host/common/ArduinoMain.cpp b/tests/host/common/ArduinoMain.cpp
index 900b865aef..54420d9e2a 100644
--- a/tests/host/common/ArduinoMain.cpp
+++ b/tests/host/common/ArduinoMain.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
-		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();
-			}
-		}
+		check_incoming_udp();
 	}
+
+	save();
+
 	return 0;
 }
-
diff --git a/tests/host/common/ArduinoMainSpiffs.cpp b/tests/host/common/ArduinoMainSpiffs.cpp
new file mode 100644
index 0000000000..1e5099d0ff
--- /dev/null
+++ b/tests/host/common/ArduinoMainSpiffs.cpp
@@ -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;
+}
+
diff --git a/tests/host/common/ArduinoMainUdp.cpp b/tests/host/common/ArduinoMainUdp.cpp
new file mode 100644
index 0000000000..99f567fe50
--- /dev/null
+++ b/tests/host/common/ArduinoMainUdp.cpp
@@ -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();
+		}
+	}
+}
diff --git a/tests/host/common/ClientContextSocket.cpp b/tests/host/common/ClientContextSocket.cpp
index 8dd55ee62e..8778a1f49f 100644
--- a/tests/host/common/ClientContextSocket.cpp
+++ b/tests/host/common/ClientContextSocket.cpp
@@ -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)
 		{
diff --git a/tests/host/common/MockSerial.cpp b/tests/host/common/MockSerial.cpp
index 19790dc48b..eedcaed115 100644
--- a/tests/host/common/MockSerial.cpp
+++ b/tests/host/common/MockSerial.cpp
@@ -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)
diff --git a/tests/host/common/UdpContextSocket.cpp b/tests/host/common/UdpContextSocket.cpp
index d30a6c1246..f257c57c94 100644
--- a/tests/host/common/UdpContextSocket.cpp
+++ b/tests/host/common/UdpContextSocket.cpp
@@ -150,12 +150,17 @@ size_t mockUDPPeekBytes (int sock, char* dst, size_t usersize, int timeout_ms, c
 	return retsize;
 }
 
-size_t mockUDPRead (int sock, char* dst, size_t size, int timeout_ms, char* ccinbuf, size_t& ccinbufsize)
+void mockUDPSwallow (size_t copied, char* ccinbuf, size_t& ccinbufsize)
 {
-	size_t copied = mockUDPPeekBytes(sock, dst, size, timeout_ms, ccinbuf, ccinbufsize);
-	// swallow (XXX use a circular buffer?)
+	// 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);
+	mockUDPSwallow(copied, ccinbuf, ccinbufsize);
 	return copied;
 }
 
diff --git a/tests/host/common/esp8266_peri.h b/tests/host/common/esp8266_peri.h
index b7b17675b8..d9dfd50ed4 100644
--- a/tests/host/common/esp8266_peri.h
+++ b/tests/host/common/esp8266_peri.h
@@ -3,5 +3,7 @@
 #define FAKE_ESP8266_PERI_H
 
 const int GPI = 0;
+const int GPO = 0;
+const int GP16I = 0;
 
 #endif
\ No newline at end of file
diff --git a/tests/host/common/include/ClientContext.h b/tests/host/common/include/ClientContext.h
index 8db6c1df0d..bbd3b06882 100644
--- a/tests/host/common/include/ClientContext.h
+++ b/tests/host/common/include/ClientContext.h
@@ -156,18 +156,28 @@ class ClientContext
 
     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 @@ class ClientContext
 
     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 @@ class ClientContext
         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;
diff --git a/tests/host/common/include/UdpContext.h b/tests/host/common/include/UdpContext.h
index 572decbb3a..ebeedb0907 100644
--- a/tests/host/common/include/UdpContext.h
+++ b/tests/host/common/include/UdpContext.h
@@ -79,20 +79,12 @@ class UdpContext
         _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 @@ class UdpContext
 
     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 {
diff --git a/tests/host/common/mock.h b/tests/host/common/mock.h
index d9c6cc3b87..b48c0eda30 100644
--- a/tests/host/common/mock.h
+++ b/tests/host/common/mock.h
@@ -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);
+int    mockConnect    (uint32_t addr, int& sock, int port);
+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
diff --git a/tests/host/common/spiffs_mock.cpp b/tests/host/common/spiffs_mock.cpp
index e63abae179..004b15fb9a 100644
--- a/tests/host/common/spiffs_mock.cpp
+++ b/tests/host/common/spiffs_mock.cpp
@@ -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)
 {
-    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);
+    if ((m_overwrite = (fs_size < 0)))
+        fs_size = -fs_size;
 
+    fprintf(stderr, "SPIFFS: %zd bytes\n", 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();
+    load();
 }
 
 SpiffsMock::~SpiffsMock()
 {
-    if (m_storage)
-        save();
+    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;
+    }
+    
+    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;
     }
-    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));
+    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);
-
-// 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]);
+    fprintf(stderr, "SPIFFS: saving %zi bytes to '%s'\n", m_fs.size(), m_storage.c_str());
 
-    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) {
diff --git a/tests/host/common/spiffs_mock.h b/tests/host/common/spiffs_mock.h
index 6b0c2c5102..a38fd820e3 100644
--- a/tests/host/common/spiffs_mock.h
+++ b/tests/host/common/spiffs_mock.h
@@ -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 @@ class SpiffsMock {
     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 */
diff --git a/tests/host/fs/test_fs.cpp b/tests/host/fs/test_fs.cpp
index c97e0f718b..70103a244c 100644
--- a/tests/host/fs/test_fs.cpp
+++ b/tests/host/fs/test_fs.cpp
@@ -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");
diff --git a/tests/run_CI_locally.sh b/tests/run_CI_locally.sh
index 90b2d77c34..5b23a2e4b5 100755
--- a/tests/run_CI_locally.sh
+++ b/tests/run_CI_locally.sh
@@ -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