1 // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include "catch.hpp"
15 #include "nvs.hpp"
16 #include "nvs_test_api.h"
17 #include "sdkconfig.h"
18 #include "spi_flash_emulation.h"
19 #include "nvs_partition_manager.hpp"
20 #include "nvs_partition.hpp"
21 #include "mbedtls/aes.h"
22 #include <sstream>
23 #include <iostream>
24 #include <fstream>
25 #include <dirent.h>
26 #include <unistd.h>
27 #include <sys/wait.h>
28 #include <string.h>
29 #include <string>
30 
31 #include "test_fixtures.hpp"
32 
33 #define TEST_ESP_ERR(rc, res) CHECK((rc) == (res))
34 #define TEST_ESP_OK(rc) CHECK((rc) == ESP_OK)
35 
36 using namespace std;
37 using namespace nvs;
38 
39 stringstream s_perf;
40 
dumpBytes(const uint8_t * data,size_t count)41 void dumpBytes(const uint8_t* data, size_t count)
42 {
43     for (uint32_t i = 0; i < count; ++i) {
44         if (i % 32 == 0) {
45             printf("%08x    ", i);
46         }
47         printf("%02x ", data[i]);
48         if ((i + 1) % 32 == 0) {
49             printf("\n");
50         }
51     }
52 }
53 
54 TEST_CASE("crc32 behaves as expected", "[nvs]")
55 {
56     Item item1;
57     item1.datatype = ItemType::I32;
58     item1.nsIndex = 1;
59     item1.crc32 = 0;
60     item1.chunkIndex = 0xff;
61     fill_n(item1.key, sizeof(item1.key), 0xbb);
62     fill_n(item1.data, sizeof(item1.data), 0xaa);
63 
64     auto crc32_1 = item1.calculateCrc32();
65 
66     Item item2 = item1;
67     item2.crc32 = crc32_1;
68 
69     CHECK(crc32_1 == item2.calculateCrc32());
70 
71     item2 = item1;
72     item2.nsIndex = 2;
73     CHECK(crc32_1 != item2.calculateCrc32());
74 
75     item2 = item1;
76     item2.datatype = ItemType::U32;
77     CHECK(crc32_1 != item2.calculateCrc32());
78 
79     item2 = item1;
80     strncpy(item2.key, "foo", Item::MAX_KEY_LENGTH);
81     CHECK(crc32_1 != item2.calculateCrc32());
82 }
83 
84 TEST_CASE("Page starting with empty flash is in uninitialized state", "[nvs]")
85 {
86     PartitionEmulationFixture f;
87     Page page;
88     CHECK(page.state() == Page::PageState::INVALID);
89     CHECK(page.load(&f.part, 0) == ESP_OK);
90     CHECK(page.state() == Page::PageState::UNINITIALIZED);
91 }
92 
93 TEST_CASE("Page can distinguish namespaces", "[nvs]")
94 {
95     PartitionEmulationFixture f;
96     Page page;
97     CHECK(page.load(&f.part, 0) == ESP_OK);
98     int32_t val1 = 0x12345678;
99     CHECK(page.writeItem(1, ItemType::I32, "intval1", &val1, sizeof(val1)) == ESP_OK);
100     int32_t val2 = 0x23456789;
101     CHECK(page.writeItem(2, ItemType::I32, "intval1", &val2, sizeof(val2)) == ESP_OK);
102 
103     int32_t readVal;
104     CHECK(page.readItem(2, ItemType::I32, "intval1", &readVal, sizeof(readVal)) == ESP_OK);
105     CHECK(readVal == val2);
106 
107 }
108 
109 TEST_CASE("Page reading with different type causes type mismatch error", "[nvs]")
110 {
111     PartitionEmulationFixture f;
112     Page page;
113     CHECK(page.load(&f.part, 0) == ESP_OK);
114     int32_t val = 0x12345678;
115     CHECK(page.writeItem(1, ItemType::I32, "intval1", &val, sizeof(val)) == ESP_OK);
116     CHECK(page.readItem(1, ItemType::U32, "intval1", &val, sizeof(val)) == ESP_ERR_NVS_TYPE_MISMATCH);
117 }
118 
119 TEST_CASE("Page when erased, it's state becomes UNITIALIZED", "[nvs]")
120 {
121     PartitionEmulationFixture f;
122     Page page;
123     CHECK(page.load(&f.part, 0) == ESP_OK);
124     int32_t val = 0x12345678;
125     CHECK(page.writeItem(1, ItemType::I32, "intval1", &val, sizeof(val)) == ESP_OK);
126     CHECK(page.erase() == ESP_OK);
127     CHECK(page.state() == Page::PageState::UNINITIALIZED);
128 }
129 
130 TEST_CASE("Page when writing and erasing, used/erased counts are updated correctly", "[nvs]")
131 {
132     PartitionEmulationFixture f;
133     Page page;
134     CHECK(page.load(&f.part, 0) == ESP_OK);
135     CHECK(page.getUsedEntryCount() == 0);
136     CHECK(page.getErasedEntryCount() == 0);
137     uint32_t foo1 = 0;
138     CHECK(page.writeItem(1, "foo1", foo1) == ESP_OK);
139     CHECK(page.getUsedEntryCount() == 1);
140     CHECK(page.writeItem(2, "foo1", foo1) == ESP_OK);
141     CHECK(page.getUsedEntryCount() == 2);
142     CHECK(page.eraseItem<uint32_t>(2, "foo1") == ESP_OK);
143     CHECK(page.getUsedEntryCount() == 1);
144     CHECK(page.getErasedEntryCount() == 1);
145     for (size_t i = 0; i < Page::ENTRY_COUNT - 2; ++i) {
146         char name[16];
147         snprintf(name, sizeof(name), "i%ld", (long int)i);
148         CHECK(page.writeItem(1, name, i) == ESP_OK);
149     }
150     CHECK(page.getUsedEntryCount() == Page::ENTRY_COUNT - 1);
151     CHECK(page.getErasedEntryCount() == 1);
152     for (size_t i = 0; i < Page::ENTRY_COUNT - 2; ++i) {
153         char name[16];
154         snprintf(name, sizeof(name), "i%ld", (long int)i);
155         CHECK(page.eraseItem(1, itemTypeOf<size_t>(), name) == ESP_OK);
156     }
157     CHECK(page.getUsedEntryCount() == 1);
158     CHECK(page.getErasedEntryCount() == Page::ENTRY_COUNT - 1);
159 }
160 
161 TEST_CASE("Page when page is full, adding an element fails", "[nvs]")
162 {
163     PartitionEmulationFixture f;
164     Page page;
165     CHECK(page.load(&f.part, 0) == ESP_OK);
166     for (size_t i = 0; i < Page::ENTRY_COUNT; ++i) {
167         char name[16];
168         snprintf(name, sizeof(name), "i%ld", (long int)i);
169         CHECK(page.writeItem(1, name, i) == ESP_OK);
170     }
171     CHECK(page.writeItem(1, "foo", 64UL) == ESP_ERR_NVS_PAGE_FULL);
172 }
173 
174 TEST_CASE("Page maintains its seq number")
175 {
176     PartitionEmulationFixture f;
177     {
178         Page page;
179         CHECK(page.load(&f.part, 0) == ESP_OK);
180         CHECK(page.setSeqNumber(123) == ESP_OK);
181         int32_t val = 42;
182         CHECK(page.writeItem(1, ItemType::I32, "dummy", &val, sizeof(val)) == ESP_OK);
183     }
184     {
185         Page page;
186         CHECK(page.load(&f.part, 0) == ESP_OK);
187         uint32_t seqno;
188         CHECK(page.getSeqNumber(seqno) == ESP_OK);
189         CHECK(seqno == 123);
190     }
191 }
192 
193 TEST_CASE("Page can write and read variable length data", "[nvs]")
194 {
195     PartitionEmulationFixture f;
196     Page page;
197     CHECK(page.load(&f.part, 0) == ESP_OK);
198     const char str[] = "foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234";
199     size_t len = strlen(str);
200     CHECK(page.writeItem(1, "stuff1", 42) == ESP_OK);
201     CHECK(page.writeItem(1, "stuff2", 1) == ESP_OK);
202     CHECK(page.writeItem(1, ItemType::SZ, "foobaar", str, len + 1) == ESP_OK);
203     CHECK(page.writeItem(1, "stuff3", 2) == ESP_OK);
204     CHECK(page.writeItem(1, ItemType::BLOB, "baz", str, len) == ESP_OK);
205     CHECK(page.writeItem(1, "stuff4", 0x7abbccdd) == ESP_OK);
206 
207     char buf[sizeof(str) + 16];
208     int32_t value;
209     CHECK(page.readItem(1, "stuff1", value) == ESP_OK);
210     CHECK(value == 42);
211     CHECK(page.readItem(1, "stuff2", value) == ESP_OK);
212     CHECK(value == 1);
213     CHECK(page.readItem(1, "stuff3", value) == ESP_OK);
214     CHECK(value == 2);
215     CHECK(page.readItem(1, "stuff4", value) == ESP_OK);
216     CHECK(value == 0x7abbccdd);
217 
218     fill_n(buf, sizeof(buf), 0xff);
219     CHECK(page.readItem(1, ItemType::SZ, "foobaar", buf, sizeof(buf)) == ESP_OK);
220     CHECK(memcmp(buf, str, strlen(str) + 1) == 0);
221 
222     fill_n(buf, sizeof(buf), 0xff);
223     CHECK(page.readItem(1, ItemType::BLOB, "baz", buf, sizeof(buf)) == ESP_OK);
224     CHECK(memcmp(buf, str, strlen(str)) == 0);
225 }
226 
227 TEST_CASE("Page different key names are distinguished even if the pointer is the same", "[nvs]")
228 {
229     PartitionEmulationFixture f;
230     Page page;
231     TEST_ESP_OK(page.load(&f.part, 0));
232     TEST_ESP_OK(page.writeItem(1, "i1", 1));
233     TEST_ESP_OK(page.writeItem(1, "i2", 2));
234     int32_t value;
235     char keyname[10] = {0};
236     for (int i = 0; i < 2; ++i) {
237         strncpy(keyname, "i1", sizeof(keyname) - 1);
238         TEST_ESP_OK(page.readItem(1, keyname, value));
239         CHECK(value == 1);
240         strncpy(keyname, "i2", sizeof(keyname) - 1);
241         TEST_ESP_OK(page.readItem(1, keyname, value));
242         CHECK(value == 2);
243     }
244 }
245 
246 TEST_CASE("Page validates key size", "[nvs]")
247 {
248     PartitionEmulationFixture f(0, 4);
249     Page page;
250     TEST_ESP_OK(page.load(&f.part, 0));
251     // 16-character key fails
252     TEST_ESP_ERR(page.writeItem(1, "0123456789123456", 1), ESP_ERR_NVS_KEY_TOO_LONG);
253     // 15-character key is okay
254     TEST_ESP_OK(page.writeItem(1, "012345678912345", 1));
255 }
256 
257 TEST_CASE("Page validates blob size", "[nvs]")
258 {
259     PartitionEmulationFixture f(0, 4);
260     Page page;
261     TEST_ESP_OK(page.load(&f.part, 0));
262 
263     char buf[4096] = { 0 };
264     // There are two potential errors here:
265     // - not enough space in the page (because one value has been written already)
266     // - value is too long
267     // Check that the second one is actually returned.
268     TEST_ESP_ERR(page.writeItem(1, ItemType::BLOB, "2", buf, Page::ENTRY_COUNT * Page::ENTRY_SIZE), ESP_ERR_NVS_VALUE_TOO_LONG);
269     // Should fail as well
270     TEST_ESP_ERR(page.writeItem(1, ItemType::BLOB, "2", buf, Page::CHUNK_MAX_SIZE + 1), ESP_ERR_NVS_VALUE_TOO_LONG);
271     TEST_ESP_OK(page.writeItem(1, ItemType::BLOB, "2", buf, Page::CHUNK_MAX_SIZE));
272 }
273 
274 TEST_CASE("Page handles invalid CRC of variable length items", "[nvs][cur]")
275 {
276     PartitionEmulationFixture f(0, 4);
277     {
278         Page page;
279         TEST_ESP_OK(page.load(&f.part, 0));
280         char buf[128] = {0};
281         TEST_ESP_OK(page.writeItem(1, ItemType::BLOB, "1", buf, sizeof(buf)));
282     }
283     // corrupt header of the item (64 is the offset of the first item in page)
284     uint32_t overwrite_buf = 0;
285     f.emu.write(64, &overwrite_buf, 4);
286     // load page again
287     {
288         Page page;
289         TEST_ESP_OK(page.load(&f.part, 0));
290     }
291 }
292 
293 class HashListTestHelper : public HashList
294 {
295     public:
getBlockCount()296         size_t getBlockCount()
297         {
298             return mBlockList.size();
299         }
300 };
301 
302 TEST_CASE("HashList is cleaned up as soon as items are erased", "[nvs]")
303 {
304     HashListTestHelper hashlist;
305     // Add items
306     const size_t count = 128;
307     for (size_t i = 0; i < count; ++i) {
308         char key[16];
309         snprintf(key, sizeof(key), "i%ld", (long int)i);
310         Item item(1, ItemType::U32, 1, key);
311         hashlist.insert(item, i);
312     }
313     INFO("Added " << count << " items, " << hashlist.getBlockCount() << " blocks");
314     // Remove them in reverse order
315     for (size_t i = count; i > 0; --i) {
316         hashlist.erase(i - 1, true);
317     }
318     CHECK(hashlist.getBlockCount() == 0);
319     // Add again
320     for (size_t i = 0; i < count; ++i) {
321         char key[16];
322         snprintf(key, sizeof(key), "i%ld", (long int)i);
323         Item item(1, ItemType::U32, 1, key);
324         hashlist.insert(item, i);
325     }
326     INFO("Added " << count << " items, " << hashlist.getBlockCount() << " blocks");
327     // Remove them in the same order
328     for (size_t i = 0; i < count; ++i) {
329         hashlist.erase(i, true);
330     }
331     CHECK(hashlist.getBlockCount() == 0);
332 }
333 
334 TEST_CASE("can init PageManager in empty flash", "[nvs]")
335 {
336     PartitionEmulationFixture f(0, 4);
337     PageManager pm;
338     CHECK(pm.load(&f.part, 0, 4) == ESP_OK);
339 }
340 
341 TEST_CASE("PageManager adds page in the correct order", "[nvs]")
342 {
343     const size_t pageCount = 8;
344     PartitionEmulationFixture f(0, pageCount);
345     uint32_t pageNo[pageCount] = { -1U, 50, 11, -1U, 23, 22, 24, 49};
346 
347     for (uint32_t i = 0; i < pageCount; ++i) {
348         Page p;
349         p.load(&f.part, i);
350         if (pageNo[i] != -1U) {
351             p.setSeqNumber(pageNo[i]);
352             p.writeItem(1, "foo", 10U);
353         }
354     }
355 
356     PageManager pageManager;
357     CHECK(pageManager.load(&f.part, 0, pageCount) == ESP_OK);
358 
359     uint32_t lastSeqNo = 0;
360     for (auto it = std::begin(pageManager); it != std::end(pageManager); ++it) {
361         uint32_t seqNo;
362         CHECK(it->getSeqNumber(seqNo) == ESP_OK);
363         CHECK(seqNo > lastSeqNo);
364     }
365 }
366 
367 TEST_CASE("can init storage in empty flash", "[nvs]")
368 {
369     PartitionEmulationFixture f(0, 8);
370     Storage storage(&f.part);
371     f.emu.setBounds(4, 8);
372     cout << "before check" << endl;
373     CHECK(storage.init(4, 4) == ESP_OK);
374     s_perf << "Time to init empty storage (4 sectors): " << f.emu.getTotalTime() << " us" << std::endl;
375 }
376 
377 TEST_CASE("storage doesn't add duplicates within one page", "[nvs]")
378 {
379     PartitionEmulationFixture f(0, 8);
380     Storage storage(&f.part);
381     f.emu.setBounds(4, 8);
382     CHECK(storage.init(4, 4) == ESP_OK);
383     int bar = 0;
384     CHECK(storage.writeItem(1, "bar", ++bar) == ESP_OK);
385     CHECK(storage.writeItem(1, "bar", ++bar) == ESP_OK);
386 
387     Page page;
388     page.load(&f.part, 4);
389     CHECK(page.getUsedEntryCount() == 1);
390     CHECK(page.getErasedEntryCount() == 1);
391 }
392 
393 TEST_CASE("can write one item a thousand times", "[nvs]")
394 {
395     PartitionEmulationFixture f(0, 8);
396     Storage storage(&f.part);
397     f.emu.setBounds(4, 8);
398     CHECK(storage.init(4, 4) == ESP_OK);
399     for (size_t i = 0; i < Page::ENTRY_COUNT * 4 * 2; ++i) {
400         REQUIRE(storage.writeItem(1, "i", static_cast<int>(i)) == ESP_OK);
401     }
402     s_perf << "Time to write one item a thousand times: " << f.emu.getTotalTime() << " us (" << f.emu.getEraseOps() << " " << f.emu.getWriteOps() << " " << f.emu.getReadOps() << " " << f.emu.getWriteBytes() << " " << f.emu.getReadBytes() << ")" << std::endl;
403 }
404 
405 TEST_CASE("storage doesn't add duplicates within multiple pages", "[nvs]")
406 {
407     PartitionEmulationFixture f(0, 8);
408     Storage storage(&f.part);
409     f.emu.setBounds(4, 8);
410     CHECK(storage.init(4, 4) == ESP_OK);
411     int bar = 0;
412     CHECK(storage.writeItem(1, "bar", ++bar) == ESP_OK);
413     for (size_t i = 0; i < Page::ENTRY_COUNT; ++i) {
414         CHECK(storage.writeItem(1, "foo", static_cast<int>(++bar)) == ESP_OK);
415     }
416     CHECK(storage.writeItem(1, "bar", ++bar) == ESP_OK);
417 
418     Page page;
419     page.load(&f.part, 4);
420     CHECK(page.findItem(1, itemTypeOf<int>(), "bar") == ESP_ERR_NVS_NOT_FOUND);
421     page.load(&f.part, 5);
422     CHECK(page.findItem(1, itemTypeOf<int>(), "bar") == ESP_OK);
423 }
424 
425 TEST_CASE("storage can find items on second page if first is not fully written and has cached search data", "[nvs]")
426 {
427     PartitionEmulationFixture f(0, 3);
428     Storage storage(&f.part);
429     CHECK(storage.init(0, 3) == ESP_OK);
430     int bar = 0;
431     uint8_t bigdata[(Page::CHUNK_MAX_SIZE - Page::ENTRY_SIZE)/2] = {0};
432     // write one big chunk of data
433     ESP_ERROR_CHECK(storage.writeItem(0, ItemType::BLOB, "1", bigdata, sizeof(bigdata)));
434     // write another big chunk of data
435     ESP_ERROR_CHECK(storage.writeItem(0, ItemType::BLOB, "2", bigdata, sizeof(bigdata)));
436 
437     // write third one; it will not fit into the first page
438     ESP_ERROR_CHECK(storage.writeItem(0, ItemType::BLOB, "3", bigdata, sizeof(bigdata)));
439 
440     size_t size;
441     ESP_ERROR_CHECK(storage.getItemDataSize(0, ItemType::BLOB, "1", size));
442     CHECK(size == sizeof(bigdata));
443     ESP_ERROR_CHECK(storage.getItemDataSize(0, ItemType::BLOB, "3", size));
444     CHECK(size == sizeof(bigdata));
445 }
446 
447 
448 TEST_CASE("can write and read variable length data lots of times", "[nvs]")
449 {
450     PartitionEmulationFixture f(0, 8);
451     Storage storage(&f.part);
452     f.emu.setBounds(4, 8);
453     CHECK(storage.init(4, 4) == ESP_OK);
454     const char str[] = "foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234";
455     char buf[sizeof(str) + 16];
456     size_t len = strlen(str);
457     for (size_t i = 0; i < Page::ENTRY_COUNT * 4 * 2; ++i) {
458         CAPTURE(i);
459         CHECK(storage.writeItem(1, ItemType::SZ, "foobaar", str, len + 1) == ESP_OK);
460         CHECK(storage.writeItem(1, "foo", static_cast<uint32_t>(i)) == ESP_OK);
461 
462         uint32_t value;
463         CHECK(storage.readItem(1, "foo", value) == ESP_OK);
464         CHECK(value == i);
465 
466         fill_n(buf, sizeof(buf), 0xff);
467         CHECK(storage.readItem(1, ItemType::SZ, "foobaar", buf, sizeof(buf)) == ESP_OK);
468         CHECK(memcmp(buf, str, strlen(str) + 1) == 0);
469     }
470     s_perf << "Time to write one string and one integer a thousand times: " << f.emu.getTotalTime() << " us (" << f.emu.getEraseOps() << " " << f.emu.getWriteOps() << " " << f.emu.getReadOps() << " " << f.emu.getWriteBytes() << " " << f.emu.getReadBytes() << ")" << std::endl;
471 }
472 
473 
474 TEST_CASE("can get length of variable length data", "[nvs]")
475 {
476     PartitionEmulationFixture f(0, 8);
477     f.emu.randomize(200);
478     Storage storage(&f.part);
479     f.emu.setBounds(4, 8);
480     CHECK(storage.init(4, 4) == ESP_OK);
481     const char str[] = "foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234foobar1234";
482     size_t len = strlen(str);
483     CHECK(storage.writeItem(1, ItemType::SZ, "foobaar", str, len + 1) == ESP_OK);
484     size_t dataSize;
485     CHECK(storage.getItemDataSize(1, ItemType::SZ, "foobaar", dataSize) == ESP_OK);
486     CHECK(dataSize == len + 1);
487 
488     CHECK(storage.writeItem(2, ItemType::BLOB, "foobaar", str, len) == ESP_OK);
489     CHECK(storage.getItemDataSize(2, ItemType::BLOB, "foobaar", dataSize) == ESP_OK);
490     CHECK(dataSize == len);
491 }
492 
493 
494 TEST_CASE("can create namespaces", "[nvs]")
495 {
496     PartitionEmulationFixture f(0, 8);
497     Storage storage(&f.part);
498     f.emu.setBounds(4, 8);
499     CHECK(storage.init(4, 4) == ESP_OK);
500     uint8_t nsi;
501     CHECK(storage.createOrOpenNamespace("wifi", false, nsi) == ESP_ERR_NVS_NOT_FOUND);
502 
503     CHECK(storage.createOrOpenNamespace("wifi", true, nsi) == ESP_OK);
504     Page page;
505     page.load(&f.part, 4);
506     CHECK(page.findItem(Page::NS_INDEX, ItemType::U8, "wifi") == ESP_OK);
507 }
508 
509 TEST_CASE("storage may become full", "[nvs]")
510 {
511     PartitionEmulationFixture f(0, 8);
512     Storage storage(&f.part);
513     f.emu.setBounds(4, 8);
514     CHECK(storage.init(4, 4) == ESP_OK);
515     for (size_t i = 0; i < Page::ENTRY_COUNT * 3; ++i) {
516         char name[Item::MAX_KEY_LENGTH + 1];
517         snprintf(name, sizeof(name), "key%05d", static_cast<int>(i));
518         REQUIRE(storage.writeItem(1, name, static_cast<int>(i)) == ESP_OK);
519     }
520     REQUIRE(storage.writeItem(1, "foo", 10) == ESP_ERR_NVS_NOT_ENOUGH_SPACE);
521 }
522 
523 TEST_CASE("can modify an item on a page which will be erased", "[nvs]")
524 {
525     PartitionEmulationFixture f(0, 8);
526     Storage storage(&f.part);
527     CHECK(storage.init(0, 2) == ESP_OK);
528     for (size_t i = 0; i < Page::ENTRY_COUNT * 3 + 1; ++i) {
529         REQUIRE(storage.writeItem(1, "foo", 42U) == ESP_OK);
530     }
531 }
532 
533 TEST_CASE("erase operations are distributed among sectors", "[nvs]")
534 {
535     const size_t sectors = 6;
536     PartitionEmulationFixture f(0, sectors);
537     Storage storage(&f.part);
538     CHECK(storage.init(0, sectors) == ESP_OK);
539 
540     /* Fill some part of storage with static values */
541     const size_t static_sectors = 2;
542     for (size_t i = 0; i < static_sectors * Page::ENTRY_COUNT; ++i) {
543         char name[Item::MAX_KEY_LENGTH];
544         snprintf(name, sizeof(name), "static%d", (int) i);
545         REQUIRE(storage.writeItem(1, name, i) == ESP_OK);
546     }
547 
548     /* Now perform many write operations */
549     const size_t write_ops = 2000;
550     for (size_t i = 0; i < write_ops; ++i) {
551         REQUIRE(storage.writeItem(1, "value", i) == ESP_OK);
552     }
553 
554     /* Check that erase counts are distributed between the remaining sectors */
555     const size_t max_erase_cnt = write_ops / Page::ENTRY_COUNT / (sectors - static_sectors) + 1;
556     for (size_t i = 0; i < sectors; ++i) {
557         auto erase_cnt = f.emu.getSectorEraseCount(i);
558         INFO("Sector " << i << " erased " << erase_cnt);
559         CHECK(erase_cnt <= max_erase_cnt);
560     }
561 }
562 
563 TEST_CASE("can erase items", "[nvs]")
564 {
565     PartitionEmulationFixture f(0, 8);
566     Storage storage(&f.part);
567     CHECK(storage.init(0, 3) == ESP_OK);
568     for (size_t i = 0; i < Page::ENTRY_COUNT * 2 - 3; ++i) {
569         char name[Item::MAX_KEY_LENGTH + 1];
570         snprintf(name, sizeof(name), "key%05d", static_cast<int>(i));
571         REQUIRE(storage.writeItem(3, name, static_cast<int>(i)) == ESP_OK);
572     }
573     CHECK(storage.writeItem(1, "foo", 32) == ESP_OK);
574     CHECK(storage.writeItem(2, "foo", 64) == ESP_OK);
575     CHECK(storage.eraseItem(2, "foo") == ESP_OK);
576     int val;
577     CHECK(storage.readItem(1, "foo", val) == ESP_OK);
578     CHECK(val == 32);
579     CHECK(storage.eraseNamespace(3) == ESP_OK);
580     CHECK(storage.readItem(2, "foo", val) == ESP_ERR_NVS_NOT_FOUND);
581     CHECK(storage.readItem(3, "key00222", val) == ESP_ERR_NVS_NOT_FOUND);
582 }
583 
584 TEST_CASE("namespace name is deep copy", "[nvs]")
585 {
586     char ns_name[16];
587     strcpy(ns_name, "const_name");
588 
589     nvs_handle_t handle_1;
590     nvs_handle_t handle_2;
591     const uint32_t NVS_FLASH_SECTOR = 6;
592     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
593     PartitionEmulationFixture f(NVS_FLASH_SECTOR,
594             NVS_FLASH_SECTOR_COUNT_MIN);
595     f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
596 
597     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part,
598             NVS_FLASH_SECTOR,
599             NVS_FLASH_SECTOR_COUNT_MIN));
600 
601     TEST_ESP_OK(nvs_open("const_name", NVS_READWRITE, &handle_1));
602     strcpy(ns_name, "just_kidding");
603 
604     CHECK(nvs_open("just_kidding", NVS_READONLY, &handle_2) == ESP_ERR_NVS_NOT_FOUND);
605 
606     nvs_close(handle_1);
607     nvs_close(handle_2);
608 
609     nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME);
610 }
611 
612 TEST_CASE("readonly handle fails on writing", "[nvs]")
613 {
614     PartitionEmulationFixture f(0, 10);
615     const char* str = "value 0123456789abcdef0123456789abcdef";
616     const uint8_t blob[8] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7};
617 
618     nvs_handle_t handle_1;
619     const uint32_t NVS_FLASH_SECTOR = 6;
620     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
621     f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
622 
623     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part,
624             NVS_FLASH_SECTOR,
625             NVS_FLASH_SECTOR_COUNT_MIN));
626 
627     // first, creating namespace...
628     TEST_ESP_OK(nvs_open("ro_ns", NVS_READWRITE, &handle_1));
629     nvs_close(handle_1);
630 
631     TEST_ESP_OK(nvs_open("ro_ns", NVS_READONLY, &handle_1));
632     TEST_ESP_ERR(nvs_set_i32(handle_1, "key", 47), ESP_ERR_NVS_READ_ONLY);
633     TEST_ESP_ERR(nvs_set_str(handle_1, "key", str), ESP_ERR_NVS_READ_ONLY);
634     TEST_ESP_ERR(nvs_set_blob(handle_1, "key", blob, 8), ESP_ERR_NVS_READ_ONLY);
635 
636     nvs_close(handle_1);
637 
638     // without deinit it affects "nvs api tests"
639     TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
640 }
641 
642 TEST_CASE("nvs api tests", "[nvs]")
643 {
644     PartitionEmulationFixture f(0, 10);
645     f.emu.randomize(100);
646 
647     nvs_handle_t handle_1;
648     const uint32_t NVS_FLASH_SECTOR = 6;
649     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
650     f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
651 
652 
653     TEST_ESP_ERR(nvs_open("namespace1", NVS_READWRITE, &handle_1), ESP_ERR_NVS_NOT_INITIALIZED);
654     for (uint16_t i = NVS_FLASH_SECTOR; i <NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++i) {
655         f.emu.erase(i);
656     }
657     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part,
658             NVS_FLASH_SECTOR,
659             NVS_FLASH_SECTOR_COUNT_MIN));
660 
661     TEST_ESP_ERR(nvs_open("namespace1", NVS_READONLY, &handle_1), ESP_ERR_NVS_NOT_FOUND);
662 
663     // TEST_ESP_ERR(nvs_set_i32(handle_1, "foo", 0x12345678), ESP_ERR_NVS_READ_ONLY);
664     // nvs_close(handle_1);
665 
666     TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle_1));
667     TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x12345678));
668     TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x23456789));
669 
670     nvs_handle_t handle_2;
671     TEST_ESP_OK(nvs_open("namespace2", NVS_READWRITE, &handle_2));
672     TEST_ESP_OK(nvs_set_i32(handle_2, "foo", 0x3456789a));
673     const char* str = "value 0123456789abcdef0123456789abcdef";
674     TEST_ESP_OK(nvs_set_str(handle_2, "key", str));
675 
676     int32_t v1;
677     TEST_ESP_OK(nvs_get_i32(handle_1, "foo", &v1));
678     CHECK(0x23456789 == v1);
679 
680     int32_t v2;
681     TEST_ESP_OK(nvs_get_i32(handle_2, "foo", &v2));
682     CHECK(0x3456789a == v2);
683 
684     char buf[strlen(str) + 1];
685     size_t buf_len = sizeof(buf);
686 
687     size_t buf_len_needed;
688     TEST_ESP_OK(nvs_get_str(handle_2, "key", NULL, &buf_len_needed));
689     CHECK(buf_len_needed == buf_len);
690 
691     size_t buf_len_short = buf_len - 1;
692     TEST_ESP_ERR(ESP_ERR_NVS_INVALID_LENGTH, nvs_get_str(handle_2, "key", buf, &buf_len_short));
693     CHECK(buf_len_short == buf_len);
694 
695     size_t buf_len_long = buf_len + 1;
696     TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len_long));
697     CHECK(buf_len_long == buf_len);
698 
699     TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len));
700 
701     CHECK(0 == strcmp(buf, str));
702     nvs_close(handle_1);
703     nvs_close(handle_2);
704 
705     TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
706 }
707 
708 TEST_CASE("deinit partition doesn't affect other partition's open handles", "[nvs]")
709 {
710     const char *OTHER_PARTITION_NAME = "other_part";
711     PartitionEmulationFixture f(0, 10);
712     PartitionEmulationFixture f_other(0, 10, OTHER_PARTITION_NAME);
713     const char* str = "value 0123456789abcdef0123456789abcdef";
714     const uint8_t blob[8] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7};
715 
716     nvs_handle_t handle_1;
717     const uint32_t NVS_FLASH_SECTOR = 6;
718     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
719     f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
720     f_other.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
721 
722     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part,
723             NVS_FLASH_SECTOR,
724             NVS_FLASH_SECTOR_COUNT_MIN));
725     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f_other.part,
726             NVS_FLASH_SECTOR,
727             NVS_FLASH_SECTOR_COUNT_MIN));
728 
729     TEST_ESP_OK(nvs_open_from_partition(OTHER_PARTITION_NAME, "ns", NVS_READWRITE, &handle_1));
730 
731     // Deinitializing must not interfere with the open handle from the other partition.
732     TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
733 
734     TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x3456789a));
735     nvs_close(handle_1);
736 
737     TEST_ESP_OK(nvs_flash_deinit_partition(OTHER_PARTITION_NAME));
738 }
739 
740 TEST_CASE("nvs iterators tests", "[nvs]")
741 {
742     PartitionEmulationFixture f(0, 5);
743 
744     const uint32_t NVS_FLASH_SECTOR = 0;
745     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 5;
746     f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
747 
748     for (uint16_t i = NVS_FLASH_SECTOR; i < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++i) {
749         f.emu.erase(i);
750     }
751     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part,
752             NVS_FLASH_SECTOR,
753             NVS_FLASH_SECTOR_COUNT_MIN));
754 
755     nvs_iterator_t it;
756     nvs_entry_info_t info;
757     nvs_handle handle_1;
758     nvs_handle handle_2;
759     const  uint32_t blob = 0x11223344;
760     const char *name_1 = "namespace1";
761     const char *name_2 = "namespace2";
762     TEST_ESP_OK(nvs_open(name_1, NVS_READWRITE, &handle_1));
763     TEST_ESP_OK(nvs_open(name_2, NVS_READWRITE, &handle_2));
764 
765     TEST_ESP_OK(nvs_set_i8(handle_1, "value1", -11));
766     TEST_ESP_OK(nvs_set_u8(handle_1, "value2", 11));
767     TEST_ESP_OK(nvs_set_i16(handle_1, "value3", 1234));
768     TEST_ESP_OK(nvs_set_u16(handle_1, "value4", -1234));
769     TEST_ESP_OK(nvs_set_i32(handle_1, "value5", -222));
770     TEST_ESP_OK(nvs_set_i32(handle_1, "value6", -222));
771     TEST_ESP_OK(nvs_set_i32(handle_1, "value7", -222));
772     TEST_ESP_OK(nvs_set_u32(handle_1, "value8", 222));
773     TEST_ESP_OK(nvs_set_u32(handle_1, "value9", 222));
774     TEST_ESP_OK(nvs_set_str(handle_1, "value10", "foo"));
775     TEST_ESP_OK(nvs_set_blob(handle_1, "value11", &blob, sizeof(blob)));
776     TEST_ESP_OK(nvs_set_i32(handle_2, "value1", -111));
777     TEST_ESP_OK(nvs_set_i32(handle_2, "value2", -111));
778     TEST_ESP_OK(nvs_set_i64(handle_2, "value3", -555));
779     TEST_ESP_OK(nvs_set_u64(handle_2, "value4", 555));
780 
__anond527bc420102(const char *part, const char *name, nvs_type_t type)781     auto entry_count = [](const char *part, const char *name, nvs_type_t type)-> int {
782         int count;
783         nvs_iterator_t it = nvs_entry_find(part, name, type);
784         for (count = 0; it != nullptr; count++) {
785             it = nvs_entry_next(it);
786         }
787         return count;
788     };
789 
790    SECTION("Number of entries found for specified namespace and type is correct")
791    {
792         CHECK(nvs_entry_find("", NULL, NVS_TYPE_ANY) == NULL);
793         CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_ANY) == 15);
794         CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_ANY) == 11);
795         CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_I32) == 3);
796         CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_I32) == 5);
797         CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_U64) == 1);
798    }
799 
800    SECTION("New entry is not created when existing key-value pair is set")
801    {
802         CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_2, NVS_TYPE_ANY) == 4);
803         TEST_ESP_OK(nvs_set_i32(handle_2, "value1", -222));
804         CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_2, NVS_TYPE_ANY) == 4);
805    }
806 
807     SECTION("Number of entries found decrease when entry is erased")
808     {
809         CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_U64) == 1);
810         TEST_ESP_OK(nvs_erase_key(handle_2, "value4"));
811         CHECK(entry_count(NVS_DEFAULT_PART_NAME, "", NVS_TYPE_U64) == 0);
812     }
813 
814     SECTION("All fields of nvs_entry_info_t structure are correct")
815     {
816         it = nvs_entry_find(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_I32);
817         CHECK(it != nullptr);
818         string key = "value5";
819         do {
820             nvs_entry_info(it, &info);
821 
822             CHECK(string(name_1) == info.namespace_name);
823             CHECK(key == info.key);
824             CHECK(info.type == NVS_TYPE_I32);
825 
826             it = nvs_entry_next(it);
827             key[5]++;
828         } while (it != NULL);
829         nvs_release_iterator(it);
830     }
831 
832     SECTION("Entry info is not affected by subsequent erase")
833     {
834         nvs_entry_info_t info_after_erase;
835 
836         it = nvs_entry_find(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_ANY);
837         nvs_entry_info(it, &info);
838         TEST_ESP_OK(nvs_erase_key(handle_1, "value1"));
839         nvs_entry_info(it, &info_after_erase);
840         CHECK(memcmp(&info, &info_after_erase, sizeof(info)) == 0);
841         nvs_release_iterator(it);
842     }
843 
844     SECTION("Entry info is not affected by subsequent set")
845     {
846         nvs_entry_info_t info_after_set;
847 
848         it = nvs_entry_find(NVS_DEFAULT_PART_NAME, name_1, NVS_TYPE_ANY);
849         nvs_entry_info(it, &info);
850         TEST_ESP_OK(nvs_set_u8(handle_1, info.key, 44));
851         nvs_entry_info(it, &info_after_set);
852         CHECK(memcmp(&info, &info_after_set, sizeof(info)) == 0);
853         nvs_release_iterator(it);
854     }
855 
856 
857     SECTION("Iterating over multiple pages works correctly")
858     {
859         nvs_handle handle_3;
860         const char *name_3 = "namespace3";
861         const int entries_created = 250;
862 
863         TEST_ESP_OK(nvs_open(name_3, NVS_READWRITE, &handle_3));
864         for  (size_t i = 0; i < entries_created; i++) {
865             TEST_ESP_OK(nvs_set_u8(handle_3, to_string(i).c_str(), 123));
866         }
867 
868         int entries_found = 0;
869         it = nvs_entry_find(NVS_DEFAULT_PART_NAME, name_3, NVS_TYPE_ANY);
870         while(it != nullptr) {
871             entries_found++;
872             it = nvs_entry_next(it);
873         }
874         CHECK(entries_created == entries_found);
875 
876         nvs_release_iterator(it);
877         nvs_close(handle_3);
878     }
879 
880     SECTION("Iterating over multi-page blob works correctly")
881     {
882         nvs_handle handle_3;
883         const char *name_3 = "namespace3";
884         const uint8_t multipage_blob[4096 * 2] = { 0 };
885         const int NUMBER_OF_ENTRIES_PER_PAGE = 125;
886         size_t occupied_entries;
887 
888         TEST_ESP_OK(nvs_open(name_3, NVS_READWRITE, &handle_3));
889         nvs_set_blob(handle_3, "blob", multipage_blob, sizeof(multipage_blob));
890         TEST_ESP_OK(nvs_get_used_entry_count(handle_3, &occupied_entries));
891         CHECK(occupied_entries > NUMBER_OF_ENTRIES_PER_PAGE *  2);
892 
893         CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_3, NVS_TYPE_BLOB) == 1);
894 
895         nvs_close(handle_3);
896     }
897 
898     nvs_close(handle_1);
899     nvs_close(handle_2);
900 
901     TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
902 }
903 
904 TEST_CASE("Iterator with not matching type iterates correctly", "[nvs]")
905 {
906     PartitionEmulationFixture f(0, 5);
907     nvs_iterator_t it;
908     nvs_handle_t my_handle;
909     const char* NAMESPACE = "test_ns_4";
910 
911     const uint32_t NVS_FLASH_SECTOR = 0;
912     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 5;
913     f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
914 
915     for (uint16_t i = NVS_FLASH_SECTOR; i < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++i) {
916         f.emu.erase(i);
917     }
918     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part,
919             NVS_FLASH_SECTOR,
920             NVS_FLASH_SECTOR_COUNT_MIN));
921 
922     // writing string to namespace (a type which spans multiple entries)
923     TEST_ESP_OK(nvs_open(NAMESPACE, NVS_READWRITE, &my_handle));
924     TEST_ESP_OK(nvs_set_str(my_handle, "test-string", "InitString0"));
925     TEST_ESP_OK(nvs_commit(my_handle));
926     nvs_close(my_handle);
927 
928     it = nvs_entry_find(NVS_DEFAULT_PART_NAME, NAMESPACE, NVS_TYPE_I32);
929     CHECK(it == NULL);
930 
931     // re-init to trigger cleaning up of broken items -> a corrupted string will be erased
932     nvs_flash_deinit();
933     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part,
934             NVS_FLASH_SECTOR,
935             NVS_FLASH_SECTOR_COUNT_MIN));
936 
937     it = nvs_entry_find(NVS_DEFAULT_PART_NAME, NAMESPACE, NVS_TYPE_STR);
938     CHECK(it != NULL);
939     nvs_release_iterator(it);
940 
941     // without deinit it affects "nvs api tests"
942     TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
943 }
944 
945 TEST_CASE("wifi test", "[nvs]")
946 {
947     PartitionEmulationFixture f(0, 10);
948     f.emu.randomize(10);
949 
950 
951     const uint32_t NVS_FLASH_SECTOR = 5;
952     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
953     f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
954     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part,
955             NVS_FLASH_SECTOR,
956             NVS_FLASH_SECTOR_COUNT_MIN));
957 
958     nvs_handle_t misc_handle;
959     TEST_ESP_OK(nvs_open("nvs.net80211", NVS_READWRITE, &misc_handle));
960     char log[33];
961     size_t log_size = sizeof(log);
962     TEST_ESP_ERR(nvs_get_str(misc_handle, "log", log, &log_size), ESP_ERR_NVS_NOT_FOUND);
963     strcpy(log, "foobarbazfizzz");
964     TEST_ESP_OK(nvs_set_str(misc_handle, "log", log));
965 
966     nvs_handle_t net80211_handle;
967     TEST_ESP_OK(nvs_open("nvs.net80211", NVS_READWRITE, &net80211_handle));
968 
969     uint8_t opmode = 2;
970     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "wifi.opmode", &opmode), ESP_ERR_NVS_NOT_FOUND);
971 
972     TEST_ESP_OK(nvs_set_u8(net80211_handle, "wifi.opmode", opmode));
973 
974     uint8_t country = 0;
975     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "wifi.country", &opmode), ESP_ERR_NVS_NOT_FOUND);
976     TEST_ESP_OK(nvs_set_u8(net80211_handle, "wifi.country", opmode));
977 
978     char ssid[36];
979     size_t size = sizeof(ssid);
980     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.ssid", ssid, &size), ESP_ERR_NVS_NOT_FOUND);
981     strcpy(ssid, "my android AP");
982     TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.ssid", ssid, size));
983 
984     char mac[6];
985     size = sizeof(mac);
986     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.mac", mac, &size), ESP_ERR_NVS_NOT_FOUND);
987     memset(mac, 0xab, 6);
988     TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.mac", mac, size));
989 
990     uint8_t authmode = 1;
991     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.authmode", &authmode), ESP_ERR_NVS_NOT_FOUND);
992     TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.authmode", authmode));
993 
994     char pswd[65];
995     size = sizeof(pswd);
996     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.pswd", pswd, &size), ESP_ERR_NVS_NOT_FOUND);
997     strcpy(pswd, "`123456788990-=");
998     TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.pswd", pswd, size));
999 
1000     char pmk[32];
1001     size = sizeof(pmk);
1002     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.pmk", pmk, &size), ESP_ERR_NVS_NOT_FOUND);
1003     memset(pmk, 1, size);
1004     TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.pmk", pmk, size));
1005 
1006     uint8_t chan = 1;
1007     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.chan", &chan), ESP_ERR_NVS_NOT_FOUND);
1008     TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.chan", chan));
1009 
1010     uint8_t autoconn = 1;
1011     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "auto.conn", &autoconn), ESP_ERR_NVS_NOT_FOUND);
1012     TEST_ESP_OK(nvs_set_u8(net80211_handle, "auto.conn", autoconn));
1013 
1014     uint8_t bssid_set = 1;
1015     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "bssid.set", &bssid_set), ESP_ERR_NVS_NOT_FOUND);
1016     TEST_ESP_OK(nvs_set_u8(net80211_handle, "bssid.set", bssid_set));
1017 
1018     char bssid[6];
1019     size = sizeof(bssid);
1020     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.bssid", bssid, &size), ESP_ERR_NVS_NOT_FOUND);
1021     memset(mac, 0xcd, 6);
1022     TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.bssid", bssid, size));
1023 
1024     uint8_t phym = 3;
1025     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.phym", &phym), ESP_ERR_NVS_NOT_FOUND);
1026     TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.phym", phym));
1027 
1028     uint8_t phybw = 2;
1029     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.phybw", &phybw), ESP_ERR_NVS_NOT_FOUND);
1030     TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.phybw", phybw));
1031 
1032     char apsw[2];
1033     size = sizeof(apsw);
1034     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.apsw", apsw, &size), ESP_ERR_NVS_NOT_FOUND);
1035     memset(apsw, 0x2, size);
1036     TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.apsw", apsw, size));
1037 
1038     char apinfo[700];
1039     size = sizeof(apinfo);
1040     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.apinfo", apinfo, &size), ESP_ERR_NVS_NOT_FOUND);
1041     memset(apinfo, 0, size);
1042     TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.apinfo", apinfo, size));
1043 
1044     size = sizeof(ssid);
1045     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.ssid", ssid, &size), ESP_ERR_NVS_NOT_FOUND);
1046     strcpy(ssid, "ESP_A2F340");
1047     TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.ssid", ssid, size));
1048 
1049     size = sizeof(mac);
1050     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.mac", mac, &size), ESP_ERR_NVS_NOT_FOUND);
1051     memset(mac, 0xac, 6);
1052     TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.mac", mac, size));
1053 
1054     size = sizeof(pswd);
1055     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.passwd", pswd, &size), ESP_ERR_NVS_NOT_FOUND);
1056     strcpy(pswd, "");
1057     TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.passwd", pswd, size));
1058 
1059     size = sizeof(pmk);
1060     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.pmk", pmk, &size), ESP_ERR_NVS_NOT_FOUND);
1061     memset(pmk, 1, size);
1062     TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.pmk", pmk, size));
1063 
1064     chan = 6;
1065     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.chan", &chan), ESP_ERR_NVS_NOT_FOUND);
1066     TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.chan", chan));
1067 
1068     authmode = 0;
1069     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.authmode", &authmode), ESP_ERR_NVS_NOT_FOUND);
1070     TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.authmode", authmode));
1071 
1072     uint8_t hidden = 0;
1073     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.hidden", &hidden), ESP_ERR_NVS_NOT_FOUND);
1074     TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.hidden", hidden));
1075 
1076     uint8_t max_conn = 4;
1077     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.max.conn", &max_conn), ESP_ERR_NVS_NOT_FOUND);
1078     TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.max.conn", max_conn));
1079 
1080     uint8_t bcn_interval = 2;
1081     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "bcn_interval", &bcn_interval), ESP_ERR_NVS_NOT_FOUND);
1082     TEST_ESP_OK(nvs_set_u8(net80211_handle, "bcn_interval", bcn_interval));
1083 
1084     s_perf << "Time to simulate nvs init with wifi libs: " << f.emu.getTotalTime() << " us (" << f.emu.getEraseOps() << "E " << f.emu.getWriteOps() << "W " << f.emu.getReadOps() << "R " << f.emu.getWriteBytes() << "Wb " << f.emu.getReadBytes() << "Rb)" << std::endl;
1085 
1086     TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
1087 }
1088 
1089 TEST_CASE("writing the identical content does not write or erase", "[nvs]")
1090 {
1091     PartitionEmulationFixture f(0, 20);
1092 
1093     const uint32_t NVS_FLASH_SECTOR = 5;
1094     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 10;
1095     f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
1096     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part,
1097             NVS_FLASH_SECTOR,
1098             NVS_FLASH_SECTOR_COUNT_MIN));
1099 
1100     nvs_handle misc_handle;
1101     TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &misc_handle));
1102 
1103     // Test writing a u8 twice, then changing it
1104     nvs_set_u8(misc_handle, "test_u8", 8);
1105     f.emu.clearStats();
1106     nvs_set_u8(misc_handle, "test_u8", 8);
1107     CHECK(f.emu.getWriteOps() == 0);
1108     CHECK(f.emu.getEraseOps() == 0);
1109     CHECK(f.emu.getReadOps()  != 0);
1110     f.emu.clearStats();
1111     nvs_set_u8(misc_handle, "test_u8", 9);
1112     CHECK(f.emu.getWriteOps() != 0);
1113     CHECK(f.emu.getReadOps()  != 0);
1114 
1115     // Test writing a string twice, then changing it
1116     static const char *test[2] = {"Hello world.", "Hello world!"};
1117     nvs_set_str(misc_handle, "test_str", test[0]);
1118     f.emu.clearStats();
1119     nvs_set_str(misc_handle, "test_str", test[0]);
1120     CHECK(f.emu.getWriteOps() == 0);
1121     CHECK(f.emu.getEraseOps() == 0);
1122     CHECK(f.emu.getReadOps()  != 0);
1123     f.emu.clearStats();
1124     nvs_set_str(misc_handle, "test_str", test[1]);
1125     CHECK(f.emu.getWriteOps() != 0);
1126     CHECK(f.emu.getReadOps()  != 0);
1127 
1128     // Test writing a multi-page blob, then changing it
1129     uint8_t blob[Page::CHUNK_MAX_SIZE * 3] = {0};
1130     memset(blob, 1, sizeof(blob));
1131     nvs_set_blob(misc_handle, "test_blob", blob, sizeof(blob));
1132     f.emu.clearStats();
1133     nvs_set_blob(misc_handle, "test_blob", blob, sizeof(blob));
1134     CHECK(f.emu.getWriteOps() == 0);
1135     CHECK(f.emu.getEraseOps() == 0);
1136     CHECK(f.emu.getReadOps()  != 0);
1137     blob[sizeof(blob) - 1]++;
1138     f.emu.clearStats();
1139     nvs_set_blob(misc_handle, "test_blob", blob, sizeof(blob));
1140     CHECK(f.emu.getWriteOps() != 0);
1141     CHECK(f.emu.getReadOps()  != 0);
1142 
1143     TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
1144 }
1145 
1146 TEST_CASE("can init storage from flash with random contents", "[nvs]")
1147 {
1148     PartitionEmulationFixture f(0, 10);
1149     f.emu.randomize(42);
1150 
1151     nvs_handle_t handle;
1152     const uint32_t NVS_FLASH_SECTOR = 5;
1153     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
1154     f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
1155     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part,
1156             NVS_FLASH_SECTOR,
1157             NVS_FLASH_SECTOR_COUNT_MIN));
1158 
1159     TEST_ESP_OK(nvs_open("nvs.net80211", NVS_READWRITE, &handle));
1160 
1161     uint8_t opmode = 2;
1162     if (nvs_get_u8(handle, "wifi.opmode", &opmode) != ESP_OK) {
1163         TEST_ESP_OK(nvs_set_u8(handle, "wifi.opmode", opmode));
1164     }
1165 
1166     TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
1167 }
1168 
1169 
1170 TEST_CASE("nvs api tests, starting with random data in flash", "[nvs][long]")
1171 {
1172     const size_t testIters = 3000;
1173     int lastPercent = -1;
1174     for (size_t count = 0; count < testIters; ++count) {
1175         int percentDone = (int) (count * 100 / testIters);
1176         if (percentDone != lastPercent) {
1177             lastPercent = percentDone;
1178             printf("%d%%\n", percentDone);
1179         }
1180         PartitionEmulationFixture f(0, 10);
1181         f.emu.randomize(static_cast<uint32_t>(count));
1182 
1183         const uint32_t NVS_FLASH_SECTOR = 6;
1184         const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
1185         f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
1186 
1187         TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part,
1188                 NVS_FLASH_SECTOR,
1189                 NVS_FLASH_SECTOR_COUNT_MIN));
1190 
1191         nvs_handle_t handle_1;
1192         TEST_ESP_ERR(nvs_open("namespace1", NVS_READONLY, &handle_1), ESP_ERR_NVS_NOT_FOUND);
1193 
1194         TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle_1));
1195         TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x12345678));
1196         for (size_t i = 0; i < 500; ++i) {
1197             nvs_handle_t handle_2;
1198             TEST_ESP_OK(nvs_open("namespace2", NVS_READWRITE, &handle_2));
1199             TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x23456789 % (i + 1)));
1200             TEST_ESP_OK(nvs_set_i32(handle_2, "foo", static_cast<int32_t>(i)));
1201             const char* str = "value 0123456789abcdef0123456789abcdef %09d";
1202             char str_buf[128];
1203             snprintf(str_buf, sizeof(str_buf), str, i + count * 1024);
1204             TEST_ESP_OK(nvs_set_str(handle_2, "key", str_buf));
1205 
1206             int32_t v1;
1207             TEST_ESP_OK(nvs_get_i32(handle_1, "foo", &v1));
1208             CHECK(0x23456789 % (i + 1) == v1);
1209 
1210             int32_t v2;
1211             TEST_ESP_OK(nvs_get_i32(handle_2, "foo", &v2));
1212             CHECK(static_cast<int32_t>(i) == v2);
1213 
1214             char buf[128];
1215             size_t buf_len = sizeof(buf);
1216 
1217             TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len));
1218 
1219             CHECK(0 == strcmp(buf, str_buf));
1220             nvs_close(handle_2);
1221         }
1222         nvs_close(handle_1);
1223     }
1224 
1225     TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
1226 }
1227 extern "C" void nvs_dump(const char *partName);
1228 
1229 class RandomTest {
1230 
1231     static const size_t nKeys = 11;
1232     int32_t v1 = 0, v2 = 0;
1233     uint64_t v3 = 0, v4 = 0;
1234     static const size_t strBufLen = 1024;
1235     static const size_t smallBlobLen = Page::CHUNK_MAX_SIZE / 3;
1236     static const size_t largeBlobLen = Page::CHUNK_MAX_SIZE * 3;
1237     char v5[strBufLen], v6[strBufLen], v7[strBufLen], v8[strBufLen], v9[strBufLen];
1238     uint8_t v10[smallBlobLen], v11[largeBlobLen];
1239     bool written[nKeys];
1240 
1241 public:
RandomTest()1242     RandomTest()
1243     {
1244         std::fill_n(written, nKeys, false);
1245     }
1246 
1247     template<typename TGen>
doRandomThings(nvs_handle_t handle,TGen gen,size_t & count)1248     esp_err_t doRandomThings(nvs_handle_t handle, TGen gen, size_t& count) {
1249 
1250         const char* keys[] = {"foo", "bar", "longkey_0123456", "another key", "param1", "param2", "param3", "param4", "param5", "singlepage", "multipage"};
1251         const ItemType types[] = {ItemType::I32, ItemType::I32, ItemType::U64, ItemType::U64, ItemType::SZ, ItemType::SZ, ItemType::SZ, ItemType::SZ, ItemType::SZ, ItemType::BLOB, ItemType::BLOB};
1252 
1253         void* values[] = {&v1, &v2, &v3, &v4, &v5, &v6, &v7, &v8, &v9, &v10, &v11};
1254 
1255         const size_t nKeys = sizeof(keys) / sizeof(keys[0]);
1256         static_assert(nKeys == sizeof(types) / sizeof(types[0]), "");
1257         static_assert(nKeys == sizeof(values) / sizeof(values[0]), "");
1258 
1259         auto randomRead = [&](size_t index) -> esp_err_t {
1260             switch (types[index]) {
1261                 case ItemType::I32:
1262                 {
1263                     int32_t val;
1264                     auto err = nvs_get_i32(handle, keys[index], &val);
1265                     if (err == ESP_ERR_FLASH_OP_FAIL) {
1266                         return err;
1267                     }
1268                     if (!written[index]) {
1269                         REQUIRE(err == ESP_ERR_NVS_NOT_FOUND);
1270                     }
1271                     else {
1272                         REQUIRE(err == ESP_OK);
1273                         REQUIRE(val == *reinterpret_cast<int32_t*>(values[index]));
1274                     }
1275                     break;
1276                 }
1277 
1278                 case ItemType::U64:
1279                 {
1280                     uint64_t val;
1281                     auto err = nvs_get_u64(handle, keys[index], &val);
1282                     if (err == ESP_ERR_FLASH_OP_FAIL) {
1283                         return err;
1284                     }
1285                     if (!written[index]) {
1286                         REQUIRE(err == ESP_ERR_NVS_NOT_FOUND);
1287                     }
1288                     else {
1289                         REQUIRE(err == ESP_OK);
1290                         REQUIRE(val == *reinterpret_cast<uint64_t*>(values[index]));
1291                     }
1292                     break;
1293                 }
1294 
1295                 case ItemType::SZ:
1296                 {
1297                     char buf[strBufLen];
1298                     size_t len = strBufLen;
1299                     auto err = nvs_get_str(handle, keys[index], buf, &len);
1300                     if (err == ESP_ERR_FLASH_OP_FAIL) {
1301                         return err;
1302                     }
1303                     if (!written[index]) {
1304                         REQUIRE(err == ESP_ERR_NVS_NOT_FOUND);
1305                     }
1306                     else {
1307                         REQUIRE(err == ESP_OK);
1308                         REQUIRE(strncmp(buf, reinterpret_cast<const char*>(values[index]), strBufLen) == 0);
1309                     }
1310                     break;
1311                 }
1312 
1313                 case ItemType::BLOB:
1314                 {
1315                     uint32_t blobBufLen = 0;
1316                     if(strncmp(keys[index],"singlepage", sizeof("singlepage")) == 0) {
1317                        blobBufLen = smallBlobLen ;
1318                     } else {
1319                        blobBufLen = largeBlobLen ;
1320 
1321                     }
1322                     uint8_t buf[blobBufLen];
1323                     memset(buf, 0, blobBufLen);
1324 
1325                     size_t len = blobBufLen;
1326                     auto err = nvs_get_blob(handle, keys[index], buf, &len);
1327                     if (err == ESP_ERR_FLASH_OP_FAIL) {
1328                         return err;
1329                     }
1330                     if (!written[index]) {
1331                         REQUIRE(err == ESP_ERR_NVS_NOT_FOUND);
1332                     }
1333                     else {
1334                         REQUIRE(err == ESP_OK);
1335                         REQUIRE(memcmp(buf, reinterpret_cast<const uint8_t*>(values[index]), blobBufLen) == 0);
1336                     }
1337                     break;
1338                 }
1339 
1340 
1341                 default:
1342                     assert(0);
1343             }
1344             return ESP_OK;
1345         };
1346 
1347         auto randomWrite = [&](size_t index) -> esp_err_t {
1348             switch (types[index]) {
1349                 case ItemType::I32:
1350                 {
1351                     int32_t val = static_cast<int32_t>(gen());
1352 
1353                     auto err = nvs_set_i32(handle, keys[index], val);
1354                     if (err == ESP_ERR_FLASH_OP_FAIL) {
1355                         return err;
1356                     }
1357                     if (err == ESP_ERR_NVS_REMOVE_FAILED) {
1358                         written[index] = true;
1359                         *reinterpret_cast<int32_t*>(values[index]) = val;
1360                         return ESP_ERR_FLASH_OP_FAIL;
1361                     }
1362                     REQUIRE(err == ESP_OK);
1363                     written[index] = true;
1364                     *reinterpret_cast<int32_t*>(values[index]) = val;
1365                     break;
1366                 }
1367 
1368                 case ItemType::U64:
1369                 {
1370                     uint64_t val = static_cast<uint64_t>(gen());
1371 
1372                     auto err = nvs_set_u64(handle, keys[index], val);
1373                     if (err == ESP_ERR_FLASH_OP_FAIL) {
1374                         return err;
1375                     }
1376                     if (err == ESP_ERR_NVS_REMOVE_FAILED) {
1377                         written[index] = true;
1378                         *reinterpret_cast<uint64_t*>(values[index]) = val;
1379                         return ESP_ERR_FLASH_OP_FAIL;
1380                     }
1381                     REQUIRE(err == ESP_OK);
1382                     written[index] = true;
1383                     *reinterpret_cast<uint64_t*>(values[index]) = val;
1384                     break;
1385                 }
1386 
1387                 case ItemType::SZ:
1388                 {
1389                     char buf[strBufLen];
1390                     size_t len = strBufLen;
1391 
1392                     size_t strLen = gen() % (strBufLen - 1);
1393                     std::generate_n(buf, strLen, [&]() -> char {
1394                         const char c = static_cast<char>(gen() % 127);
1395                         return (c < 32) ? 32 : c;
1396                     });
1397                     buf[strLen] = 0;
1398 
1399                     auto err = nvs_set_str(handle, keys[index], buf);
1400                     if (err == ESP_ERR_FLASH_OP_FAIL) {
1401                         return err;
1402                     }
1403                     if (err == ESP_ERR_NVS_REMOVE_FAILED) {
1404                         written[index] = true;
1405                         strncpy(reinterpret_cast<char*>(values[index]), buf, strBufLen);
1406                         return ESP_ERR_FLASH_OP_FAIL;
1407                     }
1408                     REQUIRE(err == ESP_OK);
1409                     written[index] = true;
1410                     strncpy(reinterpret_cast<char*>(values[index]), buf, strBufLen);
1411                     break;
1412                 }
1413 
1414                 case ItemType::BLOB:
1415                 {
1416                     uint32_t blobBufLen = 0;
1417                     if(strncmp(keys[index],"singlepage", sizeof("singlepage")) == 0) {
1418                        blobBufLen = smallBlobLen ;
1419                     } else {
1420                        blobBufLen = largeBlobLen ;
1421                     }
1422                     uint8_t buf[blobBufLen];
1423                     memset(buf, 0, blobBufLen);
1424                     size_t blobLen = gen() % blobBufLen;
1425                     std::generate_n(buf, blobLen, [&]() -> uint8_t {
1426                         return static_cast<uint8_t>(gen() % 256);
1427                     });
1428 
1429                     auto err = nvs_set_blob(handle, keys[index], buf, blobLen);
1430                     if (err == ESP_ERR_FLASH_OP_FAIL) {
1431                         return err;
1432                     }
1433                     if (err == ESP_ERR_NVS_REMOVE_FAILED) {
1434                         written[index] = true;
1435                         memcpy(reinterpret_cast<uint8_t*>(values[index]), buf, blobBufLen);
1436                         return ESP_ERR_FLASH_OP_FAIL;
1437                     }
1438                     REQUIRE(err == ESP_OK);
1439                     written[index] = true;
1440                     memcpy(reinterpret_cast<char*>(values[index]), buf, blobBufLen);
1441                     break;
1442                 }
1443 
1444                 default:
1445                     assert(0);
1446             }
1447             return ESP_OK;
1448         };
1449 
1450 
1451         for (; count != 0; --count) {
1452             size_t index = gen() % (nKeys);
1453             switch (gen() % 3) {
1454                 case 0:  // read, 1/3
1455                     if (randomRead(index) == ESP_ERR_FLASH_OP_FAIL) {
1456                         return ESP_ERR_FLASH_OP_FAIL;
1457                     }
1458                     break;
1459 
1460                 default: // write, 2/3
1461                     if (randomWrite(index) == ESP_ERR_FLASH_OP_FAIL) {
1462                         return ESP_ERR_FLASH_OP_FAIL;
1463                     }
1464                     break;
1465             }
1466         }
1467         return ESP_OK;
1468     }
1469 
handleExternalWriteAtIndex(uint8_t index,const void * value,const size_t len)1470     esp_err_t handleExternalWriteAtIndex(uint8_t index, const void* value, const size_t len ) {
1471         if(index == 9) {  /* This is only done for small-page blobs for now*/
1472             if(len > smallBlobLen) {
1473                 return ESP_FAIL;
1474             }
1475             memcpy(v10, value, len);
1476             written[index] = true;
1477             return ESP_OK;
1478         } else {
1479             return ESP_FAIL;
1480         }
1481     }
1482 };
1483 
1484 TEST_CASE("monkey test", "[nvs][monkey]")
1485 {
1486     std::random_device rd;
1487     std::mt19937 gen(rd());
1488     uint32_t seed = 3;
1489     gen.seed(seed);
1490 
1491     PartitionEmulationFixture f(0, 10);
1492     f.emu.randomize(seed);
1493     f.emu.clearStats();
1494 
1495     const uint32_t NVS_FLASH_SECTOR = 2;
1496     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 8;
1497     f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
1498 
1499     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part,
1500             NVS_FLASH_SECTOR,
1501             NVS_FLASH_SECTOR_COUNT_MIN));
1502 
1503     nvs_handle_t handle;
1504     TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle));
1505     RandomTest test;
1506     size_t count = 1000;
1507     CHECK(test.doRandomThings(handle, gen, count) == ESP_OK);
1508 
1509     s_perf << "Monkey test: nErase=" << f.emu.getEraseOps() << " nWrite=" << f.emu.getWriteOps() << std::endl;
1510 
1511     TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
1512 }
1513 
1514 TEST_CASE("test recovery from sudden poweroff", "[long][nvs][recovery][monkey]")
1515 {
1516     std::random_device rd;
1517     std::mt19937 gen(rd());
1518     uint32_t seed = 3;
1519     gen.seed(seed);
1520     const size_t iter_count = 2000;
1521 
1522     PartitionEmulationFixture f(0, 10);
1523 
1524     const uint32_t NVS_FLASH_SECTOR = 2;
1525     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 8;
1526 
1527     f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
1528 
1529     size_t totalOps = 0;
1530     int lastPercent = -1;
1531     for (uint32_t errDelay = 0; ; ++errDelay) {
1532         INFO(errDelay);
1533         f.emu.randomize(seed);
1534         f.emu.clearStats();
1535         f.emu.failAfter(errDelay);
1536         RandomTest test;
1537 
1538         if (totalOps != 0) {
1539             int percent = errDelay * 100 / totalOps;
1540             if (percent > lastPercent) {
1541                 printf("%d/%d (%d%%)\r\n", errDelay, static_cast<int>(totalOps), percent);
1542                 lastPercent = percent;
1543             }
1544         }
1545 
1546 
1547         nvs_handle_t handle;
1548         size_t count = iter_count;
1549 
1550         if (NVSPartitionManager::get_instance()->init_custom(&f.part,
1551                 NVS_FLASH_SECTOR,
1552                 NVS_FLASH_SECTOR_COUNT_MIN) == ESP_OK) {
1553             if (nvs_open("namespace1", NVS_READWRITE, &handle) == ESP_OK) {
1554                 if(test.doRandomThings(handle, gen, count) != ESP_ERR_FLASH_OP_FAIL) {
1555                     nvs_close(handle);
1556                     break;
1557                 }
1558                 nvs_close(handle);
1559             }
1560             TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
1561         }
1562 
1563         TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part,
1564                 NVS_FLASH_SECTOR,
1565                 NVS_FLASH_SECTOR_COUNT_MIN));
1566         TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle));
1567         auto res = test.doRandomThings(handle, gen, count);
1568         if (res != ESP_OK) {
1569             nvs_dump(NVS_DEFAULT_PART_NAME);
1570             CHECK(0);
1571         }
1572         nvs_close(handle);
1573         totalOps = f.emu.getEraseOps() + f.emu.getWriteBytes() / 4;
1574 
1575         TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
1576     }
1577 }
1578 TEST_CASE("test for memory leaks in open/set", "[leaks]")
1579 {
1580     PartitionEmulationFixture f(0, 10);
1581     const uint32_t NVS_FLASH_SECTOR = 6;
1582     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
1583     f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
1584     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part,
1585             NVS_FLASH_SECTOR,
1586             NVS_FLASH_SECTOR_COUNT_MIN));
1587 
1588     for (int i = 0; i < 100000; ++i) {
1589         nvs_handle_t light_handle = 0;
1590         char lightbulb[1024] = {12, 13, 14, 15, 16};
1591         TEST_ESP_OK(nvs_open("light", NVS_READWRITE, &light_handle));
1592         TEST_ESP_OK(nvs_set_blob(light_handle, "key", lightbulb, sizeof(lightbulb)));
1593         TEST_ESP_OK(nvs_commit(light_handle));
1594         nvs_close(light_handle);
1595     }
1596 
1597     TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
1598 }
1599 
1600 TEST_CASE("duplicate items are removed", "[nvs][dupes]")
1601 {
1602     PartitionEmulationFixture f(0, 3);
1603     {
1604         // create one item
1605         nvs::Page p;
1606         p.load(&f.part, 0);
1607         p.writeItem<uint8_t>(1, "opmode", 3);
1608     }
1609     {
1610         // add another two without deleting the first one
1611         nvs::Item item(1, ItemType::U8, 1, "opmode");
1612         item.data[0] = 2;
1613         item.crc32 = item.calculateCrc32();
1614         f.emu.write(3 * 32, reinterpret_cast<const uint32_t*>(&item), sizeof(item));
1615         f.emu.write(4 * 32, reinterpret_cast<const uint32_t*>(&item), sizeof(item));
1616         uint32_t mask = 0xFFFFFFEA;
1617         f.emu.write(32, &mask, 4);
1618     }
1619     {
1620         // load page and check that second item persists
1621         nvs::Storage s(&f.part);
1622         s.init(0, 3);
1623         uint8_t val;
1624         ESP_ERROR_CHECK(s.readItem(1, "opmode", val));
1625         CHECK(val == 2);
1626     }
1627     {
1628         Page p;
1629         p.load(&f.part, 0);
1630         CHECK(p.getErasedEntryCount() == 2);
1631         CHECK(p.getUsedEntryCount() == 1);
1632     }
1633 }
1634 
1635 TEST_CASE("recovery after failure to write data", "[nvs]")
1636 {
1637     PartitionEmulationFixture f(0, 3);
1638     const char str[] = "value 0123456789abcdef012345678value 0123456789abcdef012345678";
1639 
1640     // make flash write fail exactly in Page::writeEntryData
1641     f.emu.failAfter(17);
1642     {
1643         Storage storage(&f.part);
1644         TEST_ESP_OK(storage.init(0, 3));
1645 
1646         TEST_ESP_ERR(storage.writeItem(1, ItemType::SZ, "key", str, strlen(str)), ESP_ERR_FLASH_OP_FAIL);
1647 
1648         // check that repeated operations cause an error
1649         TEST_ESP_ERR(storage.writeItem(1, ItemType::SZ, "key", str, strlen(str)), ESP_ERR_NVS_INVALID_STATE);
1650 
1651         uint8_t val;
1652         TEST_ESP_ERR(storage.readItem(1, ItemType::U8, "key", &val, sizeof(val)), ESP_ERR_NVS_NOT_FOUND);
1653     }
1654     {
1655         // load page and check that data was erased
1656         Page p;
1657         p.load(&f.part, 0);
1658         CHECK(p.getErasedEntryCount() == 3);
1659         CHECK(p.getUsedEntryCount() == 0);
1660 
1661         // try to write again
1662         TEST_ESP_OK(p.writeItem(1, ItemType::SZ, "key", str, strlen(str)));
1663     }
1664 }
1665 
1666 TEST_CASE("crc errors in item header are handled", "[nvs]")
1667 {
1668     PartitionEmulationFixture f(0, 3);
1669     Storage storage(&f.part);
1670     // prepare some data
1671     TEST_ESP_OK(storage.init(0, 3));
1672     TEST_ESP_OK(storage.writeItem(0, "ns1", static_cast<uint8_t>(1)));
1673     TEST_ESP_OK(storage.writeItem(1, "value1", static_cast<uint32_t>(1)));
1674     TEST_ESP_OK(storage.writeItem(1, "value2", static_cast<uint32_t>(2)));
1675 
1676     // corrupt item header
1677     uint32_t val = 0;
1678     f.emu.write(32 * 3, &val, 4);
1679 
1680     // check that storage can recover
1681     TEST_ESP_OK(storage.init(0, 3));
1682     TEST_ESP_OK(storage.readItem(1, "value2", val));
1683     CHECK(val == 2);
1684     // check that the corrupted item is no longer present
1685     TEST_ESP_ERR(ESP_ERR_NVS_NOT_FOUND, storage.readItem(1, "value1", val));
1686 
1687     // add more items to make the page full
1688     for (size_t i = 0; i < Page::ENTRY_COUNT; ++i) {
1689         char item_name[Item::MAX_KEY_LENGTH + 1];
1690         snprintf(item_name, sizeof(item_name), "item_%ld", (long int)i);
1691         TEST_ESP_OK(storage.writeItem(1, item_name, static_cast<uint32_t>(i)));
1692     }
1693 
1694     // corrupt another item on the full page
1695     val = 0;
1696     f.emu.write(32 * 4, &val, 4);
1697 
1698     // check that storage can recover
1699     TEST_ESP_OK(storage.init(0, 3));
1700     // check that the corrupted item is no longer present
1701     TEST_ESP_ERR(ESP_ERR_NVS_NOT_FOUND, storage.readItem(1, "value2", val));
1702 }
1703 
1704 TEST_CASE("crc error in variable length item is handled", "[nvs]")
1705 {
1706     PartitionEmulationFixture f(0, 3);
1707     const uint64_t before_val = 0xbef04e;
1708     const uint64_t after_val = 0xaf7e4;
1709     // write some data
1710     {
1711         Page p;
1712         p.load(&f.part, 0);
1713         TEST_ESP_OK(p.writeItem<uint64_t>(0, "before", before_val));
1714         const char* str = "foobar";
1715         TEST_ESP_OK(p.writeItem(0, ItemType::SZ, "key", str, strlen(str)));
1716         TEST_ESP_OK(p.writeItem<uint64_t>(0, "after", after_val));
1717     }
1718     // corrupt some data
1719     uint32_t w;
1720     CHECK(f.emu.read(&w, 32 * 3 + 8, sizeof(w)));
1721     w &= 0xf000000f;
1722     CHECK(f.emu.write(32 * 3 + 8, &w, sizeof(w)));
1723     // load and check
1724     {
1725         Page p;
1726         p.load(&f.part, 0);
1727         CHECK(p.getUsedEntryCount() == 2);
1728         CHECK(p.getErasedEntryCount() == 2);
1729 
1730         uint64_t val;
1731         TEST_ESP_OK(p.readItem<uint64_t>(0, "before", val));
1732         CHECK(val == before_val);
1733         TEST_ESP_ERR(p.findItem(0, ItemType::SZ, "key"), ESP_ERR_NVS_NOT_FOUND);
1734         TEST_ESP_OK(p.readItem<uint64_t>(0, "after", val));
1735         CHECK(val == after_val);
1736     }
1737 }
1738 
1739 
1740 TEST_CASE("read/write failure (TW8406)", "[nvs]")
1741 {
1742     PartitionEmulationFixture f(0, 3);
1743     NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3);
1744     for (int attempts = 0; attempts < 3; ++attempts) {
1745         int i = 0;
1746         nvs_handle_t light_handle = 0;
1747         char key[15] = {0};
1748         char data[76] = {12, 13, 14, 15, 16};
1749         uint8_t number = 20;
1750         size_t data_len = sizeof(data);
1751 
1752         ESP_ERROR_CHECK(nvs_open("LIGHT", NVS_READWRITE, &light_handle));
1753         ESP_ERROR_CHECK(nvs_set_u8(light_handle, "RecordNum", number));
1754         for (i = 0; i < number; ++i) {
1755             sprintf(key, "light%d", i);
1756             ESP_ERROR_CHECK(nvs_set_blob(light_handle, key, data, sizeof(data)));
1757         }
1758         nvs_commit(light_handle);
1759 
1760         uint8_t get_number = 0;
1761         ESP_ERROR_CHECK(nvs_get_u8(light_handle, "RecordNum", &get_number));
1762         REQUIRE(number == get_number);
1763         for (i = 0; i < number; ++i) {
1764             char data[76] = {0};
1765             sprintf(key, "light%d", i);
1766             ESP_ERROR_CHECK(nvs_get_blob(light_handle, key, data, &data_len));
1767         }
1768         nvs_close(light_handle);
1769     }
1770 
1771     TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
1772 }
1773 
1774 TEST_CASE("nvs_flash_init checks for an empty page", "[nvs]")
1775 {
1776     const size_t blob_size = Page::CHUNK_MAX_SIZE;
1777     uint8_t blob[blob_size] = {0};
1778     PartitionEmulationFixture f(0, 8);
1779     TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5) );
1780     nvs_handle_t handle;
1781     TEST_ESP_OK( nvs_open("test", NVS_READWRITE, &handle) );
1782     // Fill first page
1783     TEST_ESP_OK( nvs_set_blob(handle, "1a", blob, blob_size) );
1784     // Fill second page
1785     TEST_ESP_OK( nvs_set_blob(handle, "2a", blob, blob_size) );
1786     // Fill third page
1787     TEST_ESP_OK( nvs_set_blob(handle, "3a", blob, blob_size) );
1788     TEST_ESP_OK( nvs_commit(handle) );
1789     nvs_close(handle);
1790     TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME));
1791     // first two pages are now full, third one is writable, last two are empty
1792     // init should fail
1793     TEST_ESP_ERR( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3),
1794             ESP_ERR_NVS_NO_FREE_PAGES );
1795 
1796     // in case this test fails, to not affect other tests
1797     nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME);
1798 }
1799 
1800 TEST_CASE("multiple partitions access check", "[nvs]")
1801 {
1802     SpiFlashEmulator emu(10);
1803     PartitionEmulation p0(&emu, 0 * SPI_FLASH_SEC_SIZE, 5 * SPI_FLASH_SEC_SIZE, "nvs1");
1804     PartitionEmulation p1(&emu, 5 * SPI_FLASH_SEC_SIZE, 5 * SPI_FLASH_SEC_SIZE, "nvs2");
1805     TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&p0, 0, 5) );
1806     TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&p1, 5, 5) );
1807     nvs_handle_t handle1, handle2;
1808     TEST_ESP_OK( nvs_open_from_partition("nvs1", "test", NVS_READWRITE, &handle1) );
1809     TEST_ESP_OK( nvs_open_from_partition("nvs2", "test", NVS_READWRITE, &handle2) );
1810     TEST_ESP_OK( nvs_set_i32(handle1, "foo", 0xdeadbeef));
1811     TEST_ESP_OK( nvs_set_i32(handle2, "foo", 0xcafebabe));
1812     int32_t v1, v2;
1813     TEST_ESP_OK( nvs_get_i32(handle1, "foo", &v1));
1814     TEST_ESP_OK( nvs_get_i32(handle2, "foo", &v2));
1815     CHECK(v1 == 0xdeadbeef);
1816     CHECK(v2 == 0xcafebabe);
1817 
1818     TEST_ESP_OK(nvs_flash_deinit_partition(p0.get_partition_name()));
1819     TEST_ESP_OK(nvs_flash_deinit_partition(p1.get_partition_name()));
1820 }
1821 
1822 TEST_CASE("nvs page selection takes into account free entries also not just erased entries", "[nvs]")
1823 {
1824     const size_t blob_size = Page::CHUNK_MAX_SIZE/2;
1825     uint8_t blob[blob_size] = {0};
1826     PartitionEmulationFixture f(0, 3);
1827     TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3) );
1828     nvs_handle_t handle;
1829     TEST_ESP_OK( nvs_open("test", NVS_READWRITE, &handle) );
1830     // Fill first page
1831     TEST_ESP_OK( nvs_set_blob(handle, "1a", blob, blob_size/3) );
1832     TEST_ESP_OK( nvs_set_blob(handle, "1b", blob, blob_size) );
1833     // Fill second page
1834     TEST_ESP_OK( nvs_set_blob(handle, "2a", blob, blob_size) );
1835     TEST_ESP_OK( nvs_set_blob(handle, "2b", blob, blob_size) );
1836 
1837     // The item below should be able to fit the first page.
1838     TEST_ESP_OK( nvs_set_blob(handle, "3a", blob, 4) );
1839     TEST_ESP_OK( nvs_commit(handle) );
1840     nvs_close(handle);
1841 
1842     TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
1843 }
1844 
1845 TEST_CASE("calculate used and free space", "[nvs]")
1846 {
1847     PartitionEmulationFixture f(0, 6);
1848     nvs_flash_deinit();
1849     TEST_ESP_ERR(nvs_get_stats(NULL, NULL), ESP_ERR_INVALID_ARG);
1850     nvs_stats_t stat1;
1851     nvs_stats_t stat2;
1852     TEST_ESP_ERR(nvs_get_stats(NULL, &stat1), ESP_ERR_NVS_NOT_INITIALIZED);
1853     CHECK(stat1.free_entries == 0);
1854     CHECK(stat1.namespace_count == 0);
1855     CHECK(stat1.total_entries == 0);
1856     CHECK(stat1.used_entries == 0);
1857 
1858     nvs_handle_t handle = 0;
1859     size_t h_count_entries;
1860     TEST_ESP_ERR(nvs_get_used_entry_count(handle, &h_count_entries), ESP_ERR_NVS_INVALID_HANDLE);
1861     CHECK(h_count_entries == 0);
1862 
1863     // init nvs
1864     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 6));
1865 
1866     TEST_ESP_ERR(nvs_get_used_entry_count(handle, &h_count_entries), ESP_ERR_NVS_INVALID_HANDLE);
1867     CHECK(h_count_entries == 0);
1868 
1869     Page p;
1870     // after erase. empty partition
1871     TEST_ESP_OK(nvs_get_stats(NULL, &stat1));
1872     CHECK(stat1.free_entries != 0);
1873     CHECK(stat1.namespace_count == 0);
1874     CHECK(stat1.total_entries == 6 * p.ENTRY_COUNT);
1875     CHECK(stat1.used_entries == 0);
1876 
1877     // create namespace test_k1
1878     nvs_handle_t handle_1;
1879     TEST_ESP_OK(nvs_open("test_k1", NVS_READWRITE, &handle_1));
1880     TEST_ESP_OK(nvs_get_stats(NULL, &stat2));
1881     CHECK(stat2.free_entries + 1 == stat1.free_entries);
1882     CHECK(stat2.namespace_count == 1);
1883     CHECK(stat2.total_entries == stat1.total_entries);
1884     CHECK(stat2.used_entries == 1);
1885 
1886     // create pair key-value com
1887     TEST_ESP_OK(nvs_set_i32(handle_1, "com", 0x12345678));
1888     TEST_ESP_OK(nvs_get_stats(NULL, &stat1));
1889     CHECK(stat1.free_entries + 1 == stat2.free_entries);
1890     CHECK(stat1.namespace_count == 1);
1891     CHECK(stat1.total_entries == stat2.total_entries);
1892     CHECK(stat1.used_entries == 2);
1893 
1894     // change value in com
1895     TEST_ESP_OK(nvs_set_i32(handle_1, "com", 0x01234567));
1896     TEST_ESP_OK(nvs_get_stats(NULL, &stat2));
1897     CHECK(stat2.free_entries == stat1.free_entries);
1898     CHECK(stat2.namespace_count == 1);
1899     CHECK(stat2.total_entries != 0);
1900     CHECK(stat2.used_entries == 2);
1901 
1902     // create pair key-value ru
1903     TEST_ESP_OK(nvs_set_i32(handle_1, "ru", 0x00FF00FF));
1904     TEST_ESP_OK(nvs_get_stats(NULL, &stat1));
1905     CHECK(stat1.free_entries + 1 == stat2.free_entries);
1906     CHECK(stat1.namespace_count == 1);
1907     CHECK(stat1.total_entries != 0);
1908     CHECK(stat1.used_entries == 3);
1909 
1910     // amount valid pair in namespace 1
1911     size_t h1_count_entries;
1912     TEST_ESP_OK(nvs_get_used_entry_count(handle_1, &h1_count_entries));
1913     CHECK(h1_count_entries == 2);
1914 
1915     nvs_handle_t handle_2;
1916     // create namespace test_k2
1917     TEST_ESP_OK(nvs_open("test_k2", NVS_READWRITE, &handle_2));
1918     TEST_ESP_OK(nvs_get_stats(NULL, &stat2));
1919     CHECK(stat2.free_entries + 1 == stat1.free_entries);
1920     CHECK(stat2.namespace_count == 2);
1921     CHECK(stat2.total_entries == stat1.total_entries);
1922     CHECK(stat2.used_entries == 4);
1923 
1924     // create pair key-value
1925     TEST_ESP_OK(nvs_set_i32(handle_2, "su1", 0x00000001));
1926     TEST_ESP_OK(nvs_set_i32(handle_2, "su2", 0x00000002));
1927     TEST_ESP_OK(nvs_set_i32(handle_2, "sus", 0x00000003));
1928     TEST_ESP_OK(nvs_get_stats(NULL, &stat1));
1929     CHECK(stat1.free_entries + 3 == stat2.free_entries);
1930     CHECK(stat1.namespace_count == 2);
1931     CHECK(stat1.total_entries == stat2.total_entries);
1932     CHECK(stat1.used_entries == 7);
1933 
1934     CHECK(stat1.total_entries == (stat1.used_entries + stat1.free_entries));
1935 
1936     // amount valid pair in namespace 2
1937     size_t h2_count_entries;
1938     TEST_ESP_OK(nvs_get_used_entry_count(handle_2, &h2_count_entries));
1939     CHECK(h2_count_entries == 3);
1940 
1941     CHECK(stat1.used_entries == (h1_count_entries + h2_count_entries + stat1.namespace_count));
1942 
1943     nvs_close(handle_1);
1944     nvs_close(handle_2);
1945 
1946     size_t temp = h2_count_entries;
1947     TEST_ESP_ERR(nvs_get_used_entry_count(handle_1, &h2_count_entries), ESP_ERR_NVS_INVALID_HANDLE);
1948     CHECK(h2_count_entries == 0);
1949     h2_count_entries = temp;
1950     TEST_ESP_ERR(nvs_get_used_entry_count(handle_1, NULL), ESP_ERR_INVALID_ARG);
1951 
1952     nvs_handle_t handle_3;
1953     // create namespace test_k3
1954     TEST_ESP_OK(nvs_open("test_k3", NVS_READWRITE, &handle_3));
1955     TEST_ESP_OK(nvs_get_stats(NULL, &stat2));
1956     CHECK(stat2.free_entries + 1 == stat1.free_entries);
1957     CHECK(stat2.namespace_count == 3);
1958     CHECK(stat2.total_entries == stat1.total_entries);
1959     CHECK(stat2.used_entries == 8);
1960 
1961     // create pair blobs
1962     uint32_t blob[12];
1963     TEST_ESP_OK(nvs_set_blob(handle_3, "bl1", &blob, sizeof(blob)));
1964     TEST_ESP_OK(nvs_get_stats(NULL, &stat1));
1965     CHECK(stat1.free_entries + 4 == stat2.free_entries);
1966     CHECK(stat1.namespace_count == 3);
1967     CHECK(stat1.total_entries == stat2.total_entries);
1968     CHECK(stat1.used_entries == 12);
1969 
1970     // amount valid pair in namespace 2
1971     size_t h3_count_entries;
1972     TEST_ESP_OK(nvs_get_used_entry_count(handle_3, &h3_count_entries));
1973     CHECK(h3_count_entries == 4);
1974 
1975     CHECK(stat1.used_entries == (h1_count_entries + h2_count_entries + h3_count_entries + stat1.namespace_count));
1976 
1977     nvs_close(handle_3);
1978 
1979     TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
1980 }
1981 
1982 // TODO: leaks memory
1983 TEST_CASE("Recovery from power-off when the entry being erased is not on active page", "[nvs]")
1984 {
1985     const size_t blob_size = Page::CHUNK_MAX_SIZE/2 ;
1986     size_t read_size = blob_size;
1987     uint8_t blob[blob_size] = {0x11};
1988     PartitionEmulationFixture f(0, 3);
1989     TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3) );
1990     nvs_handle_t handle;
1991     TEST_ESP_OK( nvs_open("test", NVS_READWRITE, &handle) );
1992 
1993     f.emu.clearStats();
1994     f.emu.failAfter(Page::CHUNK_MAX_SIZE/4 + 75);
1995     TEST_ESP_OK( nvs_set_blob(handle, "1a", blob, blob_size) );
1996     TEST_ESP_OK( nvs_set_blob(handle, "1b", blob, blob_size) );
1997 
1998     TEST_ESP_ERR( nvs_erase_key(handle, "1a"), ESP_ERR_FLASH_OP_FAIL );
1999 
2000     TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3) );
2001 
2002     /* Check 1a is erased fully*/
2003     TEST_ESP_ERR( nvs_get_blob(handle, "1a", blob, &read_size), ESP_ERR_NVS_NOT_FOUND);
2004 
2005     /* Check 2b is still accessible*/
2006     TEST_ESP_OK( nvs_get_blob(handle, "1b", blob, &read_size));
2007 
2008     nvs_close(handle);
2009 
2010     TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
2011 }
2012 
2013 // TODO: leaks memory
2014 TEST_CASE("Recovery from power-off when page is being freed.", "[nvs]")
2015 {
2016     const size_t blob_size = (Page::ENTRY_COUNT-3) * Page::ENTRY_SIZE;
2017     size_t read_size = blob_size/2;
2018     uint8_t blob[blob_size] = {0};
2019     PartitionEmulationFixture f(0, 3);
2020     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3));
2021     nvs_handle_t handle;
2022     TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &handle));
2023     // Fill first page
2024     TEST_ESP_OK(nvs_set_blob(handle, "1a", blob, blob_size/3));
2025     TEST_ESP_OK(nvs_set_blob(handle, "1b", blob, blob_size/3));
2026     TEST_ESP_OK(nvs_set_blob(handle, "1c", blob, blob_size/4));
2027     // Fill second page
2028     TEST_ESP_OK(nvs_set_blob(handle, "2a", blob, blob_size/2));
2029     TEST_ESP_OK(nvs_set_blob(handle, "2b", blob, blob_size/2));
2030 
2031     TEST_ESP_OK(nvs_erase_key(handle, "1c"));
2032 
2033     f.emu.clearStats();
2034     f.emu.failAfter(6 * Page::ENTRY_COUNT);
2035     TEST_ESP_ERR(nvs_set_blob(handle, "1d", blob, blob_size/4), ESP_ERR_FLASH_OP_FAIL);
2036 
2037     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3));
2038 
2039     read_size = blob_size/3;
2040     TEST_ESP_OK( nvs_get_blob(handle, "1a", blob, &read_size));
2041     TEST_ESP_OK( nvs_get_blob(handle, "1b", blob, &read_size));
2042 
2043     read_size = blob_size /4;
2044     TEST_ESP_ERR( nvs_get_blob(handle, "1c", blob, &read_size), ESP_ERR_NVS_NOT_FOUND);
2045     TEST_ESP_ERR( nvs_get_blob(handle, "1d", blob, &read_size), ESP_ERR_NVS_NOT_FOUND);
2046 
2047     read_size = blob_size /2;
2048     TEST_ESP_OK( nvs_get_blob(handle, "2a", blob, &read_size));
2049     TEST_ESP_OK( nvs_get_blob(handle, "2b", blob, &read_size));
2050 
2051     TEST_ESP_OK(nvs_commit(handle));
2052     nvs_close(handle);
2053 
2054     TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
2055 }
2056 
2057 TEST_CASE("Multi-page blobs are supported", "[nvs]")
2058 {
2059     const size_t blob_size = Page::CHUNK_MAX_SIZE *2;
2060     uint8_t blob[blob_size] = {0};
2061     PartitionEmulationFixture f(0, 5);
2062     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5));
2063     nvs_handle_t handle;
2064     TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &handle));
2065     TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size));
2066     TEST_ESP_OK(nvs_commit(handle));
2067     nvs_close(handle);
2068 
2069     TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
2070 }
2071 
2072 TEST_CASE("Failures are handled while storing multi-page blobs", "[nvs]")
2073 {
2074     const size_t blob_size = Page::CHUNK_MAX_SIZE *7;
2075     uint8_t blob[blob_size] = {0};
2076     PartitionEmulationFixture f(0, 5);
2077     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5));
2078     nvs_handle_t handle;
2079     TEST_ESP_OK(nvs_open("test", NVS_READWRITE, &handle));
2080     TEST_ESP_ERR(nvs_set_blob(handle, "abc", blob, blob_size), ESP_ERR_NVS_VALUE_TOO_LONG);
2081     TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, Page::CHUNK_MAX_SIZE*2));
2082     TEST_ESP_OK(nvs_commit(handle));
2083     nvs_close(handle);
2084 
2085     TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
2086 }
2087 
2088 TEST_CASE("Reading multi-page blobs", "[nvs]")
2089 {
2090     const size_t blob_size = Page::CHUNK_MAX_SIZE *3;
2091     uint8_t blob[blob_size];
2092     uint8_t blob_read[blob_size];
2093     size_t read_size = blob_size;
2094     PartitionEmulationFixture f(0, 5);
2095     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5));
2096     nvs_handle_t handle;
2097     memset(blob, 0x11, blob_size);
2098     memset(blob_read, 0xee, blob_size);
2099     TEST_ESP_OK(nvs_open("readTest", NVS_READWRITE, &handle));
2100     TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size));
2101     TEST_ESP_OK(nvs_get_blob(handle, "abc", blob_read, &read_size));
2102     CHECK(memcmp(blob, blob_read, blob_size) == 0);
2103     TEST_ESP_OK(nvs_commit(handle));
2104     nvs_close(handle);
2105 
2106     TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
2107 }
2108 
2109 TEST_CASE("Modification of values for Multi-page blobs are supported", "[nvs]")
2110 {
2111     const size_t blob_size = Page::CHUNK_MAX_SIZE *2;
2112     uint8_t blob[blob_size] = {0};
2113     uint8_t blob_read[blob_size] = {0xfe};;
2114     uint8_t blob2[blob_size] = {0x11};
2115     uint8_t blob3[blob_size] = {0x22};
2116     uint8_t blob4[blob_size] ={ 0x33};
2117     size_t read_size = blob_size;
2118     PartitionEmulationFixture f(0, 6);
2119     TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 6) );
2120     nvs_handle_t handle;
2121     memset(blob, 0x11, blob_size);
2122     memset(blob2, 0x22, blob_size);
2123     memset(blob3, 0x33, blob_size);
2124     memset(blob4, 0x44, blob_size);
2125     memset(blob_read, 0xff, blob_size);
2126     TEST_ESP_OK( nvs_open("test", NVS_READWRITE, &handle) );
2127     TEST_ESP_OK( nvs_set_blob(handle, "abc", blob, blob_size) );
2128     TEST_ESP_OK( nvs_set_blob(handle, "abc", blob2, blob_size) );
2129     TEST_ESP_OK( nvs_set_blob(handle, "abc", blob3, blob_size) );
2130     TEST_ESP_OK( nvs_set_blob(handle, "abc", blob4, blob_size) );
2131     TEST_ESP_OK( nvs_get_blob(handle, "abc", blob_read, &read_size));
2132     CHECK(memcmp(blob4, blob_read, blob_size) == 0);
2133     TEST_ESP_OK( nvs_commit(handle) );
2134     nvs_close(handle);
2135 
2136     TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
2137 }
2138 
2139 TEST_CASE("Modification from single page blob to multi-page", "[nvs]")
2140 {
2141     const size_t blob_size = Page::CHUNK_MAX_SIZE *3;
2142     uint8_t blob[blob_size] = {0};
2143     uint8_t blob_read[blob_size] = {0xff};
2144     size_t read_size = blob_size;
2145     PartitionEmulationFixture f(0, 5);
2146     TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5) );
2147     nvs_handle_t handle;
2148     TEST_ESP_OK(nvs_open("Test", NVS_READWRITE, &handle) );
2149     TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, Page::CHUNK_MAX_SIZE/2));
2150     TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size));
2151     TEST_ESP_OK(nvs_get_blob(handle, "abc", blob_read, &read_size));
2152     CHECK(memcmp(blob, blob_read, blob_size) == 0);
2153     TEST_ESP_OK(nvs_commit(handle) );
2154     nvs_close(handle);
2155 
2156     TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
2157 }
2158 
2159 TEST_CASE("Modification from  multi-page to single page", "[nvs]")
2160 {
2161     const size_t blob_size = Page::CHUNK_MAX_SIZE *3;
2162     uint8_t blob[blob_size] = {0};
2163     uint8_t blob_read[blob_size] = {0xff};
2164     size_t read_size = blob_size;
2165     PartitionEmulationFixture f(0, 5);
2166     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5) );
2167     nvs_handle_t handle;
2168     TEST_ESP_OK(nvs_open("Test", NVS_READWRITE, &handle) );
2169     TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size));
2170     TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, Page::CHUNK_MAX_SIZE/2));
2171     TEST_ESP_OK(nvs_set_blob(handle, "abc2", blob, blob_size));
2172     TEST_ESP_OK(nvs_get_blob(handle, "abc", blob_read, &read_size));
2173     CHECK(memcmp(blob, blob_read, Page::CHUNK_MAX_SIZE) == 0);
2174     TEST_ESP_OK(nvs_commit(handle) );
2175     nvs_close(handle);
2176 
2177     TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
2178 }
2179 
2180 TEST_CASE("Multi-page blob erased using nvs_erase_key should not be found when probed for just length", "[nvs]")
2181 {
2182     const size_t blob_size = Page::CHUNK_MAX_SIZE *3;
2183     uint8_t blob[blob_size] = {0};
2184     size_t read_size = blob_size;
2185     PartitionEmulationFixture f(0, 5);
2186     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 5));
2187     nvs_handle handle;
2188     TEST_ESP_OK(nvs_open("Test", NVS_READWRITE, &handle));
2189     TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size));
2190     TEST_ESP_OK(nvs_erase_key(handle, "abc"));
2191     TEST_ESP_ERR(nvs_get_blob(handle, "abc", NULL, &read_size), ESP_ERR_NVS_NOT_FOUND);
2192     TEST_ESP_OK(nvs_commit(handle));
2193     nvs_close(handle);
2194 
2195     TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
2196 }
2197 
2198 
2199 TEST_CASE("Check that orphaned blobs are erased during init", "[nvs]")
2200 {
2201     const size_t blob_size = Page::CHUNK_MAX_SIZE *3 ;
2202     uint8_t blob[blob_size] = {0x11};
2203     uint8_t blob2[blob_size] = {0x22};
2204     uint8_t blob3[blob_size] = {0x33};
2205     PartitionEmulationFixture f(0, 5);
2206     Storage storage(&f.part);
2207 
2208     TEST_ESP_OK(storage.init(0, 5));
2209 
2210     TEST_ESP_OK(storage.writeItem(1, ItemType::BLOB, "key", blob, sizeof(blob)));
2211 
2212 
2213     TEST_ESP_OK(storage.init(0, 5));
2214     /* Check that multi-page item is still available.**/
2215     TEST_ESP_OK(storage.readItem(1, ItemType::BLOB, "key", blob, sizeof(blob)));
2216 
2217     TEST_ESP_ERR(storage.writeItem(1, ItemType::BLOB, "key2", blob, sizeof(blob)), ESP_ERR_NVS_NOT_ENOUGH_SPACE);
2218 
2219     Page p;
2220     p.load(&f.part, 3); // This is where index will be placed.
2221     p.erase();
2222 
2223     TEST_ESP_OK(storage.init(0, 5));
2224 
2225     TEST_ESP_ERR(storage.readItem(1, ItemType::BLOB, "key", blob, sizeof(blob)), ESP_ERR_NVS_NOT_FOUND);
2226     TEST_ESP_OK(storage.writeItem(1, ItemType::BLOB, "key3", blob, sizeof(blob)));
2227 }
2228 
2229 TEST_CASE("nvs blob fragmentation test", "[nvs]")
2230 {
2231     PartitionEmulationFixture f(0, 4);
2232     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 4) );
2233     const size_t BLOB_SIZE = 3500;
2234     uint8_t *blob = (uint8_t*) malloc(BLOB_SIZE);
2235     CHECK(blob != NULL);
2236     memset(blob, 0xEE, BLOB_SIZE);
2237     const uint32_t magic = 0xff33eaeb;
2238     nvs_handle_t h;
2239     TEST_ESP_OK( nvs_open("blob_tests", NVS_READWRITE, &h) );
2240     for (int i = 0; i < 128; i++) {
2241         INFO("Iteration " << i << "...\n");
2242         TEST_ESP_OK( nvs_set_u32(h, "magic", magic) );
2243         TEST_ESP_OK( nvs_set_blob(h, "blob", blob, BLOB_SIZE) );
2244         char seq_buf[16];
2245         sprintf(seq_buf, "seq%d", i);
2246         TEST_ESP_OK( nvs_set_u32(h, seq_buf, i) );
2247     }
2248     free(blob);
2249 
2250     TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
2251 }
2252 
2253 TEST_CASE("nvs code handles errors properly when partition is near to full", "[nvs]")
2254 {
2255     const size_t blob_size = Page::CHUNK_MAX_SIZE * 0.3 ;
2256     uint8_t blob[blob_size] = {0x11};
2257     PartitionEmulationFixture f(0, 5);
2258     Storage storage(&f.part);
2259     char nvs_key[16] = "";
2260 
2261     TEST_ESP_OK(storage.init(0, 5));
2262 
2263     /* Four pages should fit roughly 12 blobs*/
2264     for(uint8_t count = 1; count <= 12; count++) {
2265         sprintf(nvs_key, "key:%u", count);
2266         TEST_ESP_OK(storage.writeItem(1, ItemType::BLOB, nvs_key, blob, sizeof(blob)));
2267     }
2268 
2269     for(uint8_t count = 13; count <= 20; count++) {
2270         sprintf(nvs_key, "key:%u", count);
2271         TEST_ESP_ERR(storage.writeItem(1, ItemType::BLOB, nvs_key, blob, sizeof(blob)), ESP_ERR_NVS_NOT_ENOUGH_SPACE);
2272     }
2273 }
2274 
2275 TEST_CASE("Check for nvs version incompatibility", "[nvs]")
2276 {
2277     PartitionEmulationFixture f(0, 3);
2278 
2279     int32_t val1 = 0x12345678;
2280     Page p;
2281     p.load(&f.part, 0);
2282     TEST_ESP_OK(p.setVersion(Page::NVS_VERSION - 1));
2283     TEST_ESP_OK(p.writeItem(1, ItemType::I32, "foo", &val1, sizeof(val1)));
2284 
2285     TEST_ESP_ERR(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3),
2286             ESP_ERR_NVS_NEW_VERSION_FOUND);
2287 
2288     // if something went wrong, clean up
2289     nvs_flash_deinit_partition(f.part.get_partition_name());
2290 }
2291 
2292 TEST_CASE("Check that NVS supports old blob format without blob index", "[nvs]")
2293 {
2294     SpiFlashEmulator emu("../nvs_partition_generator/part_old_blob_format.bin");
2295     PartitionEmulation part(&emu, 0, 2 * SPI_FLASH_SEC_SIZE, "test");
2296     nvs_handle_t handle;
2297 
2298     TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(&part, 0, 2) );
2299     TEST_ESP_OK( nvs_open_from_partition("test", "dummyNamespace", NVS_READWRITE, &handle));
2300 
2301     char buf[64] = {0};
2302     size_t buflen = 64;
2303     uint8_t hexdata[] = {0x01, 0x02, 0x03, 0xab, 0xcd, 0xef};
2304     TEST_ESP_OK( nvs_get_blob(handle, "dummyHex2BinKey", buf, &buflen));
2305     CHECK(memcmp(buf, hexdata, buflen) == 0);
2306 
2307     buflen = 64;
2308     uint8_t base64data[] = {'1', '2', '3', 'a', 'b', 'c'};
2309     TEST_ESP_OK( nvs_get_blob(handle, "dummyBase64Key", buf, &buflen));
2310     CHECK(memcmp(buf, base64data, buflen) == 0);
2311 
2312     Page p;
2313     p.load(&part, 0);
2314 
2315     /* Check that item is stored in old format without blob index*/
2316     TEST_ESP_OK(p.findItem(1, ItemType::BLOB, "dummyHex2BinKey"));
2317 
2318     /* Modify the blob so that it is stored in the new format*/
2319     hexdata[0] = hexdata[1] = hexdata[2] = 0x99;
2320     TEST_ESP_OK(nvs_set_blob(handle, "dummyHex2BinKey", hexdata, sizeof(hexdata)));
2321 
2322     Page p2;
2323     p2.load(&part, 0);
2324 
2325     /* Check the type of the blob. Expect type mismatch since the blob is stored in new format*/
2326     TEST_ESP_ERR(p2.findItem(1, ItemType::BLOB, "dummyHex2BinKey"), ESP_ERR_NVS_TYPE_MISMATCH);
2327 
2328     /* Check that index is present for the modified blob according to new format*/
2329     TEST_ESP_OK(p2.findItem(1, ItemType::BLOB_IDX, "dummyHex2BinKey"));
2330 
2331     /* Read the blob in new format and check the contents*/
2332     buflen = 64;
2333     TEST_ESP_OK( nvs_get_blob(handle, "dummyBase64Key", buf, &buflen));
2334     CHECK(memcmp(buf, base64data, buflen) == 0);
2335 
2336     TEST_ESP_OK(nvs_flash_deinit_partition(part.get_partition_name()));
2337 }
2338 
2339 // TODO: leaks memory
2340 TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]")
2341 {
2342     std::random_device rd;
2343     std::mt19937 gen(rd());
2344     uint32_t seed = 3;
2345     gen.seed(seed);
2346 
2347     PartitionEmulationFixture f(0, 10);
2348     f.emu.randomize(seed);
2349     f.emu.clearStats();
2350 
2351     const uint32_t NVS_FLASH_SECTOR = 2;
2352     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 8;
2353     static const size_t smallBlobLen = Page::CHUNK_MAX_SIZE / 3;
2354 
2355     f.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
2356 
2357     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part,
2358             NVS_FLASH_SECTOR,
2359             NVS_FLASH_SECTOR_COUNT_MIN));
2360 
2361     nvs_handle_t handle;
2362     TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle));
2363     RandomTest test;
2364 
2365     for ( uint8_t it = 0; it < 10; it++) {
2366         size_t count = 200;
2367 
2368         /* Erase index and chunks for the blob with "singlepage" key */
2369         for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) {
2370             Page p;
2371             p.load(&f.part, num);
2372             p.eraseItem(1, ItemType::BLOB, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY);
2373             p.eraseItem(1, ItemType::BLOB_IDX, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY);
2374             p.eraseItem(1, ItemType::BLOB_DATA, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY);
2375         }
2376 
2377         /* Now write "singlepage" blob in old format*/
2378         for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) {
2379             Page p;
2380             p.load(&f.part, num);
2381             if (p.state() == Page::PageState::ACTIVE) {
2382                 uint8_t buf[smallBlobLen];
2383                 size_t blobLen = gen() % smallBlobLen;
2384 
2385                 if(blobLen > p.getVarDataTailroom()) {
2386                     blobLen = p.getVarDataTailroom();
2387                 }
2388 
__anond527bc420602() 2389                 std::generate_n(buf, blobLen, [&]() -> uint8_t {
2390                         return static_cast<uint8_t>(gen() % 256);
2391                         });
2392 
2393                 TEST_ESP_OK(p.writeItem(1, ItemType::BLOB, "singlepage", buf, blobLen, Item::CHUNK_ANY));
2394                 TEST_ESP_OK(p.findItem(1, ItemType::BLOB, "singlepage"));
2395                 test.handleExternalWriteAtIndex(9, buf, blobLen); // This assumes "singlepage" is always at index 9
2396 
2397                 break;
2398             }
2399         }
2400 
2401         TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
2402         /* Initialize again */
2403         TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part,
2404                 NVS_FLASH_SECTOR,
2405                 NVS_FLASH_SECTOR_COUNT_MIN));
2406         TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle));
2407 
2408         /* Perform random things */
2409         auto res = test.doRandomThings(handle, gen, count);
2410         if (res != ESP_OK) {
2411             nvs_dump(NVS_DEFAULT_PART_NAME);
2412             CHECK(0);
2413         }
2414 
2415         /* Check that only one version is present for "singlepage". Its possible that last iteration did not write
2416          * anything for "singlepage". So either old version or new version should be present.*/
2417         bool oldVerPresent = false, newVerPresent = false;
2418 
2419         for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) {
2420             Page p;
2421             p.load(&f.part, num);
2422             if(!oldVerPresent && p.findItem(1, ItemType::BLOB, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY) == ESP_OK) {
2423                 oldVerPresent = true;
2424             }
2425 
2426             if(!newVerPresent && p.findItem(1, ItemType::BLOB_IDX, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY) == ESP_OK) {
2427                 newVerPresent = true;
2428             }
2429         }
2430         CHECK(oldVerPresent != newVerPresent);
2431     }
2432 
2433 
2434     TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
2435     s_perf << "Monkey test: nErase=" << f.emu.getEraseOps() << " nWrite=" << f.emu.getWriteOps() << std::endl;
2436 }
2437 
2438 TEST_CASE("Recovery from power-off during modification of blob present in old-format (same page)", "[nvs]")
2439 {
2440     std::random_device rd;
2441     std::mt19937 gen(rd());
2442     uint32_t seed = 3;
2443     gen.seed(seed);
2444 
2445     PartitionEmulationFixture f(0, 3);
2446     f.emu.clearStats();
2447 
2448     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3));
2449 
2450     nvs_handle_t handle;
2451     TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle));
2452 
2453     uint8_t hexdata[] = {0x01, 0x02, 0x03, 0xab, 0xcd, 0xef};
2454     uint8_t hexdata_old[] = {0x11, 0x12, 0x13, 0xbb, 0xcc, 0xee};
2455     size_t buflen = sizeof(hexdata);
2456     uint8_t buf[Page::CHUNK_MAX_SIZE];
2457 
2458     /* Power-off when blob was being written on the same page where its old version in old format
2459      * was present*/
2460     Page p;
2461     p.load(&f.part, 0);
2462     /* Write blob in old-format*/
2463     TEST_ESP_OK(p.writeItem(1, ItemType::BLOB, "singlepage", hexdata_old, sizeof(hexdata_old)));
2464 
2465     /* Write blob in new format*/
2466     TEST_ESP_OK(p.writeItem(1, ItemType::BLOB_DATA, "singlepage", hexdata, sizeof(hexdata), 0));
2467     /* All pages are stored. Now store the index.*/
2468     Item item;
2469     item.blobIndex.dataSize = sizeof(hexdata);
2470     item.blobIndex.chunkCount = 1;
2471     item.blobIndex.chunkStart = VerOffset::VER_0_OFFSET;
2472 
2473     TEST_ESP_OK(p.writeItem(1, ItemType::BLOB_IDX, "singlepage", item.data, sizeof(item.data)));
2474 
2475     TEST_ESP_OK(p.findItem(1, ItemType::BLOB, "singlepage"));
2476 
2477     TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
2478     /* Initialize again */
2479     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3));
2480     TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle));
2481 
2482     TEST_ESP_OK( nvs_get_blob(handle, "singlepage", buf, &buflen));
2483     CHECK(memcmp(buf, hexdata, buflen) == 0);
2484 
2485     Page p2;
2486     p2.load(&f.part, 0);
2487     TEST_ESP_ERR(p2.findItem(1, ItemType::BLOB, "singlepage"), ESP_ERR_NVS_TYPE_MISMATCH);
2488 
2489     TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
2490 }
2491 
2492 TEST_CASE("Recovery from power-off during modification of blob present in old-format (different page)", "[nvs]")
2493 {
2494     std::random_device rd;
2495     std::mt19937 gen(rd());
2496     uint32_t seed = 3;
2497     gen.seed(seed);
2498 
2499     PartitionEmulationFixture f(0, 3);
2500     f.emu.clearStats();
2501 
2502     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3));
2503 
2504     nvs_handle_t handle;
2505     TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle));
2506 
2507     uint8_t hexdata[] = {0x01, 0x02, 0x03, 0xab, 0xcd, 0xef};
2508     uint8_t hexdata_old[] = {0x11, 0x12, 0x13, 0xbb, 0xcc, 0xee};
2509     size_t buflen = sizeof(hexdata);
2510     uint8_t buf[Page::CHUNK_MAX_SIZE];
2511 
2512 
2513     /* Power-off when blob was being written on the different page where its old version in old format
2514      * was present*/
2515     Page p;
2516     p.load(&f.part, 0);
2517     /* Write blob in old-format*/
2518     TEST_ESP_OK(p.writeItem(1, ItemType::BLOB, "singlepage", hexdata_old, sizeof(hexdata_old)));
2519 
2520     /* Write blob in new format*/
2521     TEST_ESP_OK(p.writeItem(1, ItemType::BLOB_DATA, "singlepage", hexdata, sizeof(hexdata), 0));
2522     /* All pages are stored. Now store the index.*/
2523     Item item;
2524     item.blobIndex.dataSize = sizeof(hexdata);
2525     item.blobIndex.chunkCount = 1;
2526     item.blobIndex.chunkStart = VerOffset::VER_0_OFFSET;
2527     p.markFull();
2528     Page p2;
2529     p2.load(&f.part, 1);
2530     p2.setSeqNumber(1);
2531 
2532     TEST_ESP_OK(p2.writeItem(1, ItemType::BLOB_IDX, "singlepage", item.data, sizeof(item.data)));
2533 
2534     TEST_ESP_OK(p.findItem(1, ItemType::BLOB, "singlepage"));
2535 
2536     TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
2537     /* Initialize again */
2538     TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(&f.part, 0, 3));
2539     TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle));
2540 
2541     TEST_ESP_OK( nvs_get_blob(handle, "singlepage", buf, &buflen));
2542     CHECK(memcmp(buf, hexdata, buflen) == 0);
2543 
2544     Page p3;
2545     p3.load(&f.part, 0);
2546     TEST_ESP_ERR(p3.findItem(1, ItemType::BLOB, "singlepage"), ESP_ERR_NVS_NOT_FOUND);
2547 
2548     TEST_ESP_OK(nvs_flash_deinit_partition(f.part.get_partition_name()));
2549 }
2550 
check_nvs_part_gen_args(SpiFlashEmulator * spi_flash_emulator,char const * part_name,int size,char const * filename,bool is_encr,nvs_sec_cfg_t * xts_cfg)2551 static void check_nvs_part_gen_args(SpiFlashEmulator *spi_flash_emulator,
2552         char const *part_name,
2553         int size,
2554         char const *filename,
2555         bool is_encr,
2556         nvs_sec_cfg_t* xts_cfg)
2557 {
2558     nvs_handle_t handle;
2559 
2560     esp_partition_t esp_part;
2561     esp_part.encrypted = false; // we're not testing generic flash encryption here, only the legacy NVS encryption
2562     esp_part.address = 0;
2563     esp_part.size = size * SPI_FLASH_SEC_SIZE;
2564     strncpy(esp_part.label, part_name, PART_NAME_MAX_SIZE);
2565     shared_ptr<Partition> part;
2566 
2567     if (is_encr) {
2568         NVSEncryptedPartition *enc_part = new NVSEncryptedPartition(&esp_part);
2569         TEST_ESP_OK(enc_part->init(xts_cfg));
2570         part.reset(enc_part);
2571     } else {
2572         part.reset(new PartitionEmulation(spi_flash_emulator, 0, size, part_name));
2573     }
2574 
2575     TEST_ESP_OK( NVSPartitionManager::get_instance()->init_custom(part.get(), 0, size) );
2576 
2577     TEST_ESP_OK( nvs_open_from_partition(part_name, "dummyNamespace", NVS_READONLY, &handle));
2578     uint8_t u8v;
2579     TEST_ESP_OK( nvs_get_u8(handle, "dummyU8Key", &u8v));
2580     CHECK(u8v == 127);
2581     int8_t i8v;
2582     TEST_ESP_OK( nvs_get_i8(handle, "dummyI8Key", &i8v));
2583     CHECK(i8v == -128);
2584     uint16_t u16v;
2585     TEST_ESP_OK( nvs_get_u16(handle, "dummyU16Key", &u16v));
2586     CHECK(u16v == 32768);
2587     uint32_t u32v;
2588     TEST_ESP_OK( nvs_get_u32(handle, "dummyU32Key", &u32v));
2589     CHECK(u32v == 4294967295);
2590     int32_t i32v;
2591     TEST_ESP_OK( nvs_get_i32(handle, "dummyI32Key", &i32v));
2592     CHECK(i32v == -2147483648);
2593 
2594     char buf[64] = {0};
2595     size_t buflen = 64;
2596     TEST_ESP_OK( nvs_get_str(handle, "dummyStringKey", buf, &buflen));
2597     CHECK(strncmp(buf, "0A:0B:0C:0D:0E:0F", buflen) == 0);
2598 
2599     uint8_t hexdata[] = {0x01, 0x02, 0x03, 0xab, 0xcd, 0xef};
2600     buflen = 64;
2601     int j;
2602     TEST_ESP_OK( nvs_get_blob(handle, "dummyHex2BinKey", buf, &buflen));
2603     CHECK(memcmp(buf, hexdata, buflen) == 0);
2604 
2605     uint8_t base64data[] = {'1', '2', '3', 'a', 'b', 'c'};
2606     TEST_ESP_OK( nvs_get_blob(handle, "dummyBase64Key", buf, &buflen));
2607     CHECK(memcmp(buf, base64data, buflen) == 0);
2608 
2609     buflen = 64;
2610     uint8_t hexfiledata[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
2611     TEST_ESP_OK( nvs_get_blob(handle, "hexFileKey", buf, &buflen));
2612     CHECK(memcmp(buf, hexfiledata, buflen) == 0);
2613 
2614     buflen = 64;
2615     uint8_t strfiledata[64] = "abcdefghijklmnopqrstuvwxyz\0";
2616     TEST_ESP_OK( nvs_get_str(handle, "stringFileKey", buf, &buflen));
2617     CHECK(memcmp(buf, strfiledata, buflen) == 0);
2618 
2619     char bin_data[5200];
2620     size_t bin_len = sizeof(bin_data);
2621     char binfiledata[5200];
2622     ifstream file;
2623     file.open(filename);
2624     file.read(binfiledata,5200);
2625     TEST_ESP_OK( nvs_get_blob(handle, "binFileKey", bin_data, &bin_len));
2626     CHECK(memcmp(bin_data, binfiledata, bin_len) == 0);
2627 
2628     file.close();
2629 
2630     nvs_close(handle);
2631 
2632     TEST_ESP_OK(nvs_flash_deinit_partition(part_name));
2633 }
2634 
2635 
2636 TEST_CASE("check and read data from partition generated via partition generation utility with multipage blob support disabled", "[nvs_part_gen]")
2637 {
2638     int status;
2639     int childpid = fork();
2640     if (childpid == 0) {
2641         exit(execlp("cp", " cp",
2642                     "-rf",
2643                     "../nvs_partition_generator/testdata",
2644                     ".",NULL));
2645     } else {
2646         CHECK(childpid > 0);
2647         waitpid(childpid, &status, 0);
2648         CHECK(WEXITSTATUS(status) != -1);
2649 
2650         childpid = fork();
2651 
2652         if (childpid == 0) {
2653             exit(execlp("python", "python",
2654                     "../nvs_partition_generator/nvs_partition_gen.py",
2655                     "generate",
2656                     "../nvs_partition_generator/sample_singlepage_blob.csv",
2657                     "partition_single_page.bin",
2658                     "0x3000",
2659                     "--version",
2660                     "1",
2661                     "--outdir",
2662                     "../nvs_partition_generator",NULL));
2663         } else {
2664             CHECK(childpid > 0);
2665             int status;
2666             waitpid(childpid, &status, 0);
2667             CHECK(WEXITSTATUS(status) == 0);
2668         }
2669     }
2670 
2671     SpiFlashEmulator emu("../nvs_partition_generator/partition_single_page.bin");
2672 
2673     check_nvs_part_gen_args(&emu, "test", 3, "../nvs_partition_generator/testdata/sample_singlepage_blob.bin", false, NULL);
2674 
2675     childpid = fork();
2676     if (childpid == 0) {
2677         exit(execlp("rm", " rm",
2678                     "-rf",
2679                     "testdata",NULL));
2680     } else {
2681         CHECK(childpid > 0);
2682         waitpid(childpid, &status, 0);
2683         CHECK(WEXITSTATUS(status) == 0);
2684 
2685     }
2686 }
2687 
2688 TEST_CASE("check and read data from partition generated via partition generation utility with multipage blob support enabled", "[nvs_part_gen]")
2689 {
2690     int status;
2691     int childpid = fork();
2692     if (childpid == 0) {
2693         exit(execlp("cp", " cp",
2694                     "-rf",
2695                     "../nvs_partition_generator/testdata",
2696                     ".",NULL));
2697     } else {
2698         CHECK(childpid > 0);
2699         waitpid(childpid, &status, 0);
2700         CHECK(WEXITSTATUS(status) == 0);
2701 
2702         childpid = fork();
2703 
2704         if (childpid == 0) {
2705             exit(execlp("python", "python",
2706                     "../nvs_partition_generator/nvs_partition_gen.py",
2707                     "generate",
2708                     "../nvs_partition_generator/sample_multipage_blob.csv",
2709                     "partition_multipage_blob.bin",
2710                     "0x4000",
2711                     "--version",
2712                     "2",
2713                     "--outdir",
2714                     "../nvs_partition_generator",NULL));
2715         } else {
2716             CHECK(childpid > 0);
2717             waitpid(childpid, &status, 0);
2718             CHECK(WEXITSTATUS(status) == 0);
2719         }
2720     }
2721 
2722     SpiFlashEmulator emu("../nvs_partition_generator/partition_multipage_blob.bin");
2723 
2724     check_nvs_part_gen_args(&emu, "test", 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin",false,NULL);
2725 
2726     childpid = fork();
2727     if (childpid == 0) {
2728         exit(execlp("rm", " rm",
2729                     "-rf",
2730                     "testdata",NULL));
2731     } else {
2732         CHECK(childpid > 0);
2733         waitpid(childpid, &status, 0);
2734         CHECK(WEXITSTATUS(status) == 0);
2735 
2736     }
2737 }
2738 
2739 TEST_CASE("check and read data from partition generated via manufacturing utility with multipage blob support disabled", "[mfg_gen]")
2740 {
2741     int childpid = fork();
2742     int status;
2743 
2744     if (childpid == 0) {
2745         exit(execlp("bash", "bash",
2746                     "-c",
2747                     "rm -rf ../../../tools/mass_mfg/host_test && \
2748                     cp -rf ../../../tools/mass_mfg/testdata mfg_testdata && \
2749                     cp -rf ../nvs_partition_generator/testdata . && \
2750                     mkdir -p ../../../tools/mass_mfg/host_test", NULL));
2751     } else {
2752         CHECK(childpid > 0);
2753         waitpid(childpid, &status, 0);
2754         CHECK(WEXITSTATUS(status) == 0);
2755 
2756         childpid = fork();
2757         if (childpid == 0) {
2758             exit(execlp("python", "python",
2759                         "../../../tools/mass_mfg/mfg_gen.py",
2760                         "generate",
2761                         "../../../tools/mass_mfg/samples/sample_config.csv",
2762                         "../../../tools/mass_mfg/samples/sample_values_singlepage_blob.csv",
2763                         "Test",
2764                         "0x3000",
2765                         "--outdir",
2766                         "../../../tools/mass_mfg/host_test",
2767                         "--version",
2768                         "1",NULL));
2769 
2770         } else {
2771             CHECK(childpid > 0);
2772             waitpid(childpid, &status, 0);
2773             CHECK(WEXITSTATUS(status) == 0);
2774 
2775             childpid = fork();
2776             if (childpid == 0) {
2777                 exit(execlp("python", "python",
2778                             "../nvs_partition_generator/nvs_partition_gen.py",
2779                             "generate",
2780                             "../../../tools/mass_mfg/host_test/csv/Test-1.csv",
2781                             "../nvs_partition_generator/Test-1-partition.bin",
2782                             "0x3000",
2783                             "--version",
2784                             "1",NULL));
2785 
2786             } else {
2787                 CHECK(childpid > 0);
2788                 waitpid(childpid, &status, 0);
2789                 CHECK(WEXITSTATUS(status) == 0);
2790 
2791             }
2792 
2793         }
2794 
2795     }
2796 
2797     SpiFlashEmulator emu1("../../../tools/mass_mfg/host_test/bin/Test-1.bin");
2798     check_nvs_part_gen_args(&emu1, "test", 3, "mfg_testdata/sample_singlepage_blob.bin", false, NULL);
2799 
2800     SpiFlashEmulator emu2("../nvs_partition_generator/Test-1-partition.bin");
2801     check_nvs_part_gen_args(&emu2, "test", 3, "testdata/sample_singlepage_blob.bin", false, NULL);
2802 
2803 
2804     childpid = fork();
2805     if (childpid == 0) {
2806         exit(execlp("bash", " bash",
2807                     "-c",
2808                     "rm -rf ../../../tools/mass_mfg/host_test | \
2809                     rm -rf mfg_testdata | \
2810                     rm -rf testdata",NULL));
2811     } else {
2812         CHECK(childpid > 0);
2813         waitpid(childpid, &status, 0);
2814         CHECK(WEXITSTATUS(status) == 0);
2815 
2816     }
2817 
2818 }
2819 
2820 TEST_CASE("check and read data from partition generated via manufacturing utility with multipage blob support enabled", "[mfg_gen]")
2821 {
2822     int childpid = fork();
2823     int status;
2824 
2825     if (childpid == 0) {
2826         exit(execlp("bash", " bash",
2827                     "-c",
2828                     "rm -rf ../../../tools/mass_mfg/host_test | \
2829                     cp -rf ../../../tools/mass_mfg/testdata mfg_testdata | \
2830                     cp -rf ../nvs_partition_generator/testdata . | \
2831                     mkdir -p ../../../tools/mass_mfg/host_test",NULL));
2832     } else {
2833         CHECK(childpid > 0);
2834         waitpid(childpid, &status, 0);
2835         CHECK(WEXITSTATUS(status) == 0);
2836 
2837         childpid = fork();
2838         if (childpid == 0) {
2839             exit(execlp("python", "python",
2840                         "../../../tools/mass_mfg/mfg_gen.py",
2841                         "generate",
2842                         "../../../tools/mass_mfg/samples/sample_config.csv",
2843                         "../../../tools/mass_mfg/samples/sample_values_multipage_blob.csv",
2844                         "Test",
2845                         "0x4000",
2846                         "--outdir",
2847                         "../../../tools/mass_mfg/host_test",
2848                         "--version",
2849                         "2",NULL));
2850 
2851         } else {
2852             CHECK(childpid > 0);
2853             waitpid(childpid, &status, 0);
2854             CHECK(WEXITSTATUS(status) == 0);
2855 
2856             childpid = fork();
2857             if (childpid == 0) {
2858                 exit(execlp("python", "python",
2859                             "../nvs_partition_generator/nvs_partition_gen.py",
2860                             "generate",
2861                             "../../../tools/mass_mfg/host_test/csv/Test-1.csv",
2862                             "../nvs_partition_generator/Test-1-partition.bin",
2863                             "0x4000",
2864                             "--version",
2865                             "2",NULL));
2866 
2867             } else {
2868                 CHECK(childpid > 0);
2869                 waitpid(childpid, &status, 0);
2870                 CHECK(WEXITSTATUS(status) == 0);
2871 
2872             }
2873 
2874         }
2875 
2876     }
2877 
2878     SpiFlashEmulator emu1("../../../tools/mass_mfg/host_test/bin/Test-1.bin");
2879     check_nvs_part_gen_args(&emu1, "test", 4, "mfg_testdata/sample_multipage_blob.bin", false, NULL);
2880 
2881     SpiFlashEmulator emu2("../nvs_partition_generator/Test-1-partition.bin");
2882     check_nvs_part_gen_args(&emu2, "test", 4, "testdata/sample_multipage_blob.bin", false, NULL);
2883 
2884     childpid = fork();
2885     if (childpid == 0) {
2886         exit(execlp("bash", " bash",
2887                     "-c",
2888                     "rm -rf ../../../tools/mass_mfg/host_test | \
2889                     rm -rf mfg_testdata | \
2890                     rm -rf testdata",NULL));
2891     } else {
2892         CHECK(childpid > 0);
2893         waitpid(childpid, &status, 0);
2894         CHECK(WEXITSTATUS(status) == 0);
2895 
2896     }
2897 
2898 }
2899 
2900 #if CONFIG_NVS_ENCRYPTION
2901 TEST_CASE("check underlying xts code for 32-byte size sector encryption", "[nvs]")
2902 {
__anond527bc420702(char ch) 2903     auto toHex = [](char ch) {
2904         if(ch >= '0' && ch <= '9')
2905             return ch - '0';
2906         else if(ch >= 'a' && ch <= 'f')
2907             return ch - 'a' + 10;
2908         else if(ch >= 'A' && ch <= 'F')
2909             return ch - 'A' + 10;
2910         else
2911             return 0;
2912     };
2913 
__anond527bc420802(char* c) 2914     auto toHexByte = [toHex](char* c) {
2915         return 16 * toHex(c[0]) + toHex(c[1]);
2916     };
2917 
__anond527bc420902(char* src, uint8_t* dest) 2918     auto toHexStream = [toHexByte](char* src, uint8_t* dest) {
2919         uint32_t cnt =0;
2920         char* p = src;
2921         while(*p != '\0' && *(p + 1) != '\0')
2922         {
2923             dest[cnt++] = toHexByte(p); p += 2;
2924         }
2925     };
2926 
2927     uint8_t eky_hex[2 * NVS_KEY_SIZE];
2928     uint8_t ptxt_hex[Page::ENTRY_SIZE], ctxt_hex[Page::ENTRY_SIZE], ba_hex[16];
2929     mbedtls_aes_xts_context ectx[1];
2930     mbedtls_aes_xts_context dctx[1];
2931 
2932     char eky[][2 * NVS_KEY_SIZE + 1] = {
2933         "0000000000000000000000000000000000000000000000000000000000000000",
2934         "1111111111111111111111111111111111111111111111111111111111111111"
2935     };
2936     char tky[][2 * NVS_KEY_SIZE + 1] = {
2937         "0000000000000000000000000000000000000000000000000000000000000000",
2938         "2222222222222222222222222222222222222222222222222222222222222222"
2939     };
2940     char blk_addr[][2*16 + 1]  = {
2941         "00000000000000000000000000000000",
2942         "33333333330000000000000000000000"
2943     };
2944 
2945     char ptxt[][2 * Page::ENTRY_SIZE + 1] = {
2946         "0000000000000000000000000000000000000000000000000000000000000000",
2947         "4444444444444444444444444444444444444444444444444444444444444444"
2948     };
2949     char ctxt[][2 * Page::ENTRY_SIZE + 1] = {
2950         "d456b4fc2e620bba6ffbed27b956c9543454dd49ebd8d8ee6f94b65cbe158f73",
2951         "e622334f184bbce129a25b2ac76b3d92abf98e22df5bdd15af471f3db8946a85"
2952     };
2953 
2954     mbedtls_aes_xts_init(ectx);
2955     mbedtls_aes_xts_init(dctx);
2956 
2957     for(uint8_t cnt = 0; cnt < sizeof(eky)/sizeof(eky[0]); cnt++) {
2958         toHexStream(eky[cnt], eky_hex);
2959         toHexStream(tky[cnt], &eky_hex[NVS_KEY_SIZE]);
2960         toHexStream(ptxt[cnt], ptxt_hex);
2961         toHexStream(ctxt[cnt], ctxt_hex);
2962         toHexStream(blk_addr[cnt], ba_hex);
2963 
2964         CHECK(!mbedtls_aes_xts_setkey_enc(ectx, eky_hex, 2 * NVS_KEY_SIZE * 8));
2965         CHECK(!mbedtls_aes_xts_setkey_enc(dctx, eky_hex, 2 * NVS_KEY_SIZE * 8));
2966 
2967         CHECK(!mbedtls_aes_crypt_xts(ectx, MBEDTLS_AES_ENCRYPT, Page::ENTRY_SIZE, ba_hex, ptxt_hex, ptxt_hex));
2968 
2969         CHECK(!memcmp(ptxt_hex, ctxt_hex, Page::ENTRY_SIZE));
2970     }
2971 }
2972 
2973 TEST_CASE("test nvs apis with encryption enabled", "[nvs]")
2974 {
2975     nvs_handle_t handle_1;
2976     const uint32_t NVS_FLASH_SECTOR = 6;
2977     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
2978 
2979     nvs_sec_cfg_t xts_cfg;
2980     for(int count = 0; count < NVS_KEY_SIZE; count++) {
2981         xts_cfg.eky[count] = 0x11;
2982         xts_cfg.tky[count] = 0x22;
2983     }
2984     EncryptedPartitionFixture fixture(&xts_cfg, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN);
2985     fixture.emu.randomize(100);
2986     fixture.emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
2987 
2988     for (uint16_t i = NVS_FLASH_SECTOR; i <NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++i) {
2989         fixture.emu.erase(i);
2990     }
2991     TEST_ESP_OK(NVSPartitionManager::get_instance()->
2992             init_custom(&fixture.part, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN));
2993 
2994     TEST_ESP_ERR(nvs_open("namespace1", NVS_READONLY, &handle_1), ESP_ERR_NVS_NOT_FOUND);
2995 
2996     TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle_1));
2997     TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x12345678));
2998     TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x23456789));
2999 
3000     nvs_handle_t handle_2;
3001     TEST_ESP_OK(nvs_open("namespace2", NVS_READWRITE, &handle_2));
3002     TEST_ESP_OK(nvs_set_i32(handle_2, "foo", 0x3456789a));
3003     const char* str = "value 0123456789abcdef0123456789abcdef";
3004     TEST_ESP_OK(nvs_set_str(handle_2, "key", str));
3005 
3006     int32_t v1;
3007     TEST_ESP_OK(nvs_get_i32(handle_1, "foo", &v1));
3008     CHECK(0x23456789 == v1);
3009 
3010     int32_t v2;
3011     TEST_ESP_OK(nvs_get_i32(handle_2, "foo", &v2));
3012     CHECK(0x3456789a == v2);
3013 
3014     char buf[strlen(str) + 1];
3015     size_t buf_len = sizeof(buf);
3016 
3017     size_t buf_len_needed;
3018     TEST_ESP_OK(nvs_get_str(handle_2, "key", NULL, &buf_len_needed));
3019     CHECK(buf_len_needed == buf_len);
3020 
3021     size_t buf_len_short = buf_len - 1;
3022     TEST_ESP_ERR(ESP_ERR_NVS_INVALID_LENGTH, nvs_get_str(handle_2, "key", buf, &buf_len_short));
3023     CHECK(buf_len_short == buf_len);
3024 
3025     size_t buf_len_long = buf_len + 1;
3026     TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len_long));
3027     CHECK(buf_len_long == buf_len);
3028 
3029     TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len));
3030 
3031     CHECK(0 == strcmp(buf, str));
3032     nvs_close(handle_1);
3033     nvs_close(handle_2);
3034     TEST_ESP_OK(nvs_flash_deinit());
3035 
3036 }
3037 
3038 TEST_CASE("test nvs apis for nvs partition generator utility with encryption enabled", "[nvs_part_gen]")
3039 {
3040     int status;
3041     int childpid = fork();
3042     if (childpid == 0) {
3043         exit(execlp("cp", " cp",
3044                     "-rf",
3045                     "../nvs_partition_generator/testdata",
3046                     ".",NULL));
3047     } else {
3048         CHECK(childpid > 0);
3049         waitpid(childpid, &status, 0);
3050         CHECK(WEXITSTATUS(status) == 0);
3051 
3052         childpid = fork();
3053 
3054         if (childpid == 0) {
3055             exit(execlp("python", "python",
3056                     "../nvs_partition_generator/nvs_partition_gen.py",
3057                     "encrypt",
3058                     "../nvs_partition_generator/sample_multipage_blob.csv",
3059                     "partition_encrypted.bin",
3060                     "0x4000",
3061                     "--inputkey",
3062                     "../nvs_partition_generator/testdata/sample_encryption_keys.bin",
3063                     "--outdir",
3064                     "../nvs_partition_generator",NULL));
3065         } else {
3066             CHECK(childpid > 0);
3067             waitpid(childpid, &status, 0);
3068             CHECK(WEXITSTATUS(status) == 0);
3069         }
3070     }
3071 
3072     SpiFlashEmulator emu("../nvs_partition_generator/partition_encrypted.bin");
3073 
3074     nvs_sec_cfg_t cfg;
3075     for(int count = 0; count < NVS_KEY_SIZE; count++) {
3076         cfg.eky[count] = 0x11;
3077         cfg.tky[count] = 0x22;
3078     }
3079 
3080     check_nvs_part_gen_args(&emu, NVS_DEFAULT_PART_NAME, 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", true, &cfg);
3081 
3082     childpid = fork();
3083     if (childpid == 0) {
3084         exit(execlp("rm", " rm",
3085                     "-rf",
3086                     "testdata",NULL));
3087     } else {
3088         CHECK(childpid > 0);
3089         waitpid(childpid, &status, 0);
3090         CHECK(WEXITSTATUS(status) == 0);
3091 
3092     }
3093 
3094 }
3095 
3096 TEST_CASE("test decrypt functionality for encrypted data", "[nvs_part_gen]")
3097 {
3098 
3099     //retrieving the temporary test data
3100     int status = system("cp -rf ../nvs_partition_generator/testdata .");
3101     CHECK(status == 0);
3102 
3103     //encoding data from sample_multipage_blob.csv
3104     status = system("python ../nvs_partition_generator/nvs_partition_gen.py generate ../nvs_partition_generator/sample_multipage_blob.csv partition_encoded.bin 0x5000 --outdir ../nvs_partition_generator");
3105     CHECK(status == 0);
3106 
3107     //encrypting data from sample_multipage_blob.csv
3108     status = system("python ../nvs_partition_generator/nvs_partition_gen.py encrypt ../nvs_partition_generator/sample_multipage_blob.csv partition_encrypted.bin 0x5000 --inputkey ../nvs_partition_generator/testdata/sample_encryption_keys.bin --outdir ../nvs_partition_generator");
3109     CHECK(status == 0);
3110 
3111     //decrypting data from partition_encrypted.bin
3112     status = system("python ../nvs_partition_generator/nvs_partition_gen.py decrypt ../nvs_partition_generator/partition_encrypted.bin ../nvs_partition_generator/testdata/sample_encryption_keys.bin ../nvs_partition_generator/partition_decrypted.bin");
3113     CHECK(status == 0);
3114 
3115     status = system("diff ../nvs_partition_generator/partition_decrypted.bin ../nvs_partition_generator/partition_encoded.bin");
3116     CHECK(status == 0);
3117     CHECK(WEXITSTATUS(status) == 0);
3118 
3119 
3120     //cleaning up the temporary test data
3121     status = system("rm -rf testdata");
3122     CHECK(status == 0);
3123 
3124 }
3125 
3126 TEST_CASE("test nvs apis for nvs partition generator utility with encryption enabled using keygen", "[nvs_part_gen]")
3127 {
3128     int childpid = fork();
3129     int status;
3130 
3131     if (childpid == 0) {
3132         exit(execlp("cp", " cp",
3133                     "-rf",
3134                     "../nvs_partition_generator/testdata",
3135                     ".",NULL));
3136     } else {
3137         CHECK(childpid > 0);
3138         waitpid(childpid, &status, 0);
3139         CHECK(WEXITSTATUS(status) == 0);
3140 
3141         childpid = fork();
3142 
3143         if (childpid == 0) {
3144             exit(execlp("rm", " rm",
3145                         "-rf",
3146                         "../nvs_partition_generator/keys",NULL));
3147         } else {
3148             CHECK(childpid > 0);
3149             waitpid(childpid, &status, 0);
3150             CHECK(WEXITSTATUS(status) == 0);
3151 
3152             childpid = fork();
3153             if (childpid == 0) {
3154                 exit(execlp("python", "python",
3155                             "../nvs_partition_generator/nvs_partition_gen.py",
3156                             "encrypt",
3157                             "../nvs_partition_generator/sample_multipage_blob.csv",
3158                             "partition_encrypted_using_keygen.bin",
3159                             "0x4000",
3160                             "--keygen",
3161                             "--outdir",
3162                             "../nvs_partition_generator",NULL));
3163 
3164             } else {
3165                 CHECK(childpid > 0);
3166                 waitpid(childpid, &status, 0);
3167                 CHECK(WEXITSTATUS(status) == 0);
3168 
3169             }
3170         }
3171     }
3172 
3173 
3174     DIR *dir;
3175     struct dirent *file;
3176     char *filename;
3177     char *files;
3178     char *file_ext;
3179 
3180     dir = opendir("../nvs_partition_generator/keys");
3181     while ((file = readdir(dir)) != NULL)
3182     {
3183         filename = file->d_name;
3184         files = strrchr(filename, '.');
3185         if (files != NULL)
3186         {
3187             file_ext = files+1;
3188             if (strncmp(file_ext,"bin",3) == 0)
3189             {
3190                 break;
3191             }
3192         }
3193     }
3194 
3195     std::string encr_file = std::string("../nvs_partition_generator/keys/") + std::string(filename);
3196     SpiFlashEmulator emu("../nvs_partition_generator/partition_encrypted_using_keygen.bin");
3197 
3198     char buffer[64];
3199     FILE *fp;
3200 
3201     fp = fopen(encr_file.c_str(),"rb");
3202     fread(buffer,sizeof(buffer),1,fp);
3203 
3204     fclose(fp);
3205 
3206     nvs_sec_cfg_t cfg;
3207 
3208     for(int count = 0; count < NVS_KEY_SIZE; count++) {
3209         cfg.eky[count] = buffer[count] & 255;
3210         cfg.tky[count] = buffer[count+32] & 255;
3211     }
3212 
3213     check_nvs_part_gen_args(&emu, NVS_DEFAULT_PART_NAME, 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", true, &cfg);
3214 
3215 }
3216 
3217 TEST_CASE("test nvs apis for nvs partition generator utility with encryption enabled using inputkey", "[nvs_part_gen]")
3218 {
3219     int childpid = fork();
3220     int status;
3221 
3222     DIR *dir;
3223     struct dirent *file;
3224     char *filename;
3225     char *files;
3226     char *file_ext;
3227 
3228     dir = opendir("../nvs_partition_generator/keys");
3229     while ((file = readdir(dir)) != NULL)
3230     {
3231         filename = file->d_name;
3232         files = strrchr(filename, '.');
3233         if (files != NULL)
3234         {
3235             file_ext = files+1;
3236             if (strncmp(file_ext,"bin",3) == 0)
3237             {
3238                 break;
3239             }
3240         }
3241     }
3242 
3243     std::string encr_file = std::string("../nvs_partition_generator/keys/") + std::string(filename);
3244 
3245      if (childpid == 0) {
3246         exit(execlp("python", "python",
3247                 "../nvs_partition_generator/nvs_partition_gen.py",
3248                 "encrypt",
3249                 "../nvs_partition_generator/sample_multipage_blob.csv",
3250                 "partition_encrypted_using_keyfile.bin",
3251                 "0x4000",
3252                 "--inputkey",
3253                 encr_file.c_str(),
3254                 "--outdir",
3255                 "../nvs_partition_generator",NULL));
3256 
3257     } else {
3258         CHECK(childpid > 0);
3259         waitpid(childpid, &status, 0);
3260         CHECK(WEXITSTATUS(status) == 0);
3261     }
3262 
3263     SpiFlashEmulator emu("../nvs_partition_generator/partition_encrypted_using_keyfile.bin");
3264 
3265     char buffer[64];
3266     FILE *fp;
3267 
3268     fp = fopen(encr_file.c_str(),"rb");
3269     fread(buffer,sizeof(buffer),1,fp);
3270 
3271     fclose(fp);
3272 
3273     nvs_sec_cfg_t cfg;
3274 
3275     for(int count = 0; count < NVS_KEY_SIZE; count++) {
3276         cfg.eky[count] = buffer[count] & 255;
3277         cfg.tky[count] = buffer[count+32] & 255;
3278     }
3279 
3280     check_nvs_part_gen_args(&emu, NVS_DEFAULT_PART_NAME, 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", true, &cfg);
3281 
3282     childpid = fork();
3283     if (childpid == 0) {
3284         exit(execlp("rm", " rm",
3285                     "-rf",
3286                     "../nvs_partition_generator/keys",NULL));
3287     } else {
3288         CHECK(childpid > 0);
3289         waitpid(childpid, &status, 0);
3290         CHECK(WEXITSTATUS(status) == 0);
3291 
3292         childpid = fork();
3293 
3294         if (childpid == 0) {
3295             exit(execlp("rm", " rm",
3296                         "-rf",
3297                         "testdata",NULL));
3298         } else {
3299             CHECK(childpid > 0);
3300             waitpid(childpid, &status, 0);
3301             CHECK(WEXITSTATUS(status) == 0);
3302         }
3303     }
3304 
3305 }
3306 
3307 TEST_CASE("check and read data from partition generated via manufacturing utility with encryption enabled using sample inputkey", "[mfg_gen]")
3308 {
3309     int childpid = fork();
3310     int status;
3311 
3312     if (childpid == 0) {
3313         exit(execlp("bash", " bash",
3314                     "-c",
3315                     "rm -rf ../../../tools/mass_mfg/host_test | \
3316                     cp -rf ../../../tools/mass_mfg/testdata mfg_testdata | \
3317                     cp -rf ../nvs_partition_generator/testdata . | \
3318                     mkdir -p ../../../tools/mass_mfg/host_test",NULL));
3319     } else {
3320         CHECK(childpid > 0);
3321         waitpid(childpid, &status, 0);
3322         CHECK(WEXITSTATUS(status) == 0);
3323 
3324         childpid = fork();
3325         if (childpid == 0) {
3326             exit(execlp("python", "python",
3327                         "../../../tools/mass_mfg/mfg_gen.py",
3328                         "generate",
3329                         "../../../tools/mass_mfg/samples/sample_config.csv",
3330                         "../../../tools/mass_mfg/samples/sample_values_multipage_blob.csv",
3331                         "Test",
3332                         "0x4000",
3333                         "--outdir",
3334                         "../../../tools/mass_mfg/host_test",
3335                         "--version",
3336                         "2",
3337                         "--inputkey",
3338                         "mfg_testdata/sample_encryption_keys.bin",NULL));
3339 
3340         } else {
3341             CHECK(childpid > 0);
3342             waitpid(childpid, &status, 0);
3343             CHECK(WEXITSTATUS(status) == 0);
3344 
3345             childpid = fork();
3346             if (childpid == 0) {
3347                 exit(execlp("python", "python",
3348                             "../nvs_partition_generator/nvs_partition_gen.py",
3349                             "encrypt",
3350                             "../../../tools/mass_mfg/host_test/csv/Test-1.csv",
3351                             "../nvs_partition_generator/Test-1-partition-encrypted.bin",
3352                             "0x4000",
3353                             "--version",
3354                             "2",
3355                             "--inputkey",
3356                             "testdata/sample_encryption_keys.bin",NULL));
3357 
3358             } else {
3359                 CHECK(childpid > 0);
3360                 waitpid(childpid, &status, 0);
3361                 CHECK(WEXITSTATUS(status) == 0);
3362 
3363             }
3364 
3365         }
3366 
3367     }
3368 
3369     SpiFlashEmulator emu1("../../../tools/mass_mfg/host_test/bin/Test-1.bin");
3370 
3371     nvs_sec_cfg_t cfg;
3372     for(int count = 0; count < NVS_KEY_SIZE; count++) {
3373         cfg.eky[count] = 0x11;
3374         cfg.tky[count] = 0x22;
3375     }
3376 
3377     check_nvs_part_gen_args(&emu1, NVS_DEFAULT_PART_NAME, 4, "mfg_testdata/sample_multipage_blob.bin", true, &cfg);
3378 
3379     SpiFlashEmulator emu2("../nvs_partition_generator/Test-1-partition-encrypted.bin");
3380 
3381     check_nvs_part_gen_args(&emu2, NVS_DEFAULT_PART_NAME, 4, "testdata/sample_multipage_blob.bin", true, &cfg);
3382 
3383 
3384     childpid = fork();
3385     if (childpid == 0) {
3386         exit(execlp("bash", " bash",
3387                     "-c",
3388                     "rm -rf ../../../tools/mass_mfg/host_test | \
3389                     rm -rf mfg_testdata | \
3390                     rm -rf testdata",NULL));
3391     } else {
3392         CHECK(childpid > 0);
3393         waitpid(childpid, &status, 0);
3394         CHECK(WEXITSTATUS(status) == 0);
3395 
3396     }
3397 
3398 }
3399 
3400 TEST_CASE("check and read data from partition generated via manufacturing utility with encryption enabled using new generated key", "[mfg_gen]")
3401 {
3402     int childpid = fork();
3403     int status;
3404 
3405     if (childpid == 0) {
3406         exit(execlp("bash", " bash",
3407                     "-c",
3408                     "rm -rf ../../../tools/mass_mfg/host_test | \
3409                     cp -rf ../../../tools/mass_mfg/testdata mfg_testdata | \
3410                     cp -rf ../nvs_partition_generator/testdata . | \
3411                     mkdir -p ../../../tools/mass_mfg/host_test",NULL));
3412     } else {
3413         CHECK(childpid > 0);
3414         waitpid(childpid, &status, 0);
3415         CHECK(WEXITSTATUS(status) == 0);
3416 
3417         childpid = fork();
3418         if (childpid == 0) {
3419             exit(execlp("python", "python",
3420                         "../../../tools/mass_mfg/mfg_gen.py",
3421                         "generate-key",
3422                         "--outdir",
3423                         "../../../tools/mass_mfg/host_test",
3424                         "--keyfile",
3425                         "encr_keys_host_test.bin",NULL));
3426 
3427         } else {
3428             CHECK(childpid > 0);
3429             waitpid(childpid, &status, 0);
3430             CHECK(WEXITSTATUS(status) == 0);
3431 
3432             childpid = fork();
3433             if (childpid == 0) {
3434                 exit(execlp("python", "python",
3435                             "../../../tools/mass_mfg/mfg_gen.py",
3436                             "generate",
3437                             "../../../tools/mass_mfg/samples/sample_config.csv",
3438                             "../../../tools/mass_mfg/samples/sample_values_multipage_blob.csv",
3439                             "Test",
3440                             "0x4000",
3441                             "--outdir",
3442                             "../../../tools/mass_mfg/host_test",
3443                             "--version",
3444                             "2",
3445                             "--inputkey",
3446                             "../../../tools/mass_mfg/host_test/keys/encr_keys_host_test.bin",NULL));
3447 
3448             } else {
3449                 CHECK(childpid > 0);
3450                 waitpid(childpid, &status, 0);
3451                 CHECK(WEXITSTATUS(status) == 0);
3452 
3453                 childpid = fork();
3454                 if (childpid == 0) {
3455                     exit(execlp("python", "python",
3456                                 "../nvs_partition_generator/nvs_partition_gen.py",
3457                                 "encrypt",
3458                                 "../../../tools/mass_mfg/host_test/csv/Test-1.csv",
3459                                 "../nvs_partition_generator/Test-1-partition-encrypted.bin",
3460                                 "0x4000",
3461                                 "--version",
3462                                 "2",
3463                                 "--inputkey",
3464                                 "../../../tools/mass_mfg/host_test/keys/encr_keys_host_test.bin",NULL));
3465 
3466                 } else {
3467                     CHECK(childpid > 0);
3468                     waitpid(childpid, &status, 0);
3469                     CHECK(WEXITSTATUS(status) == 0);
3470 
3471                 }
3472 
3473             }
3474 
3475         }
3476 
3477     }
3478 
3479 
3480     SpiFlashEmulator emu1("../../../tools/mass_mfg/host_test/bin/Test-1.bin");
3481 
3482     char buffer[64];
3483     FILE *fp;
3484 
3485     fp = fopen("../../../tools/mass_mfg/host_test/keys/encr_keys_host_test.bin","rb");
3486     fread(buffer,sizeof(buffer),1,fp);
3487 
3488     fclose(fp);
3489 
3490     nvs_sec_cfg_t cfg;
3491 
3492     for(int count = 0; count < NVS_KEY_SIZE; count++) {
3493         cfg.eky[count] = buffer[count] & 255;
3494         cfg.tky[count] = buffer[count+32] & 255;
3495     }
3496 
3497     check_nvs_part_gen_args(&emu1, NVS_DEFAULT_PART_NAME, 4, "mfg_testdata/sample_multipage_blob.bin", true, &cfg);
3498 
3499     SpiFlashEmulator emu2("../nvs_partition_generator/Test-1-partition-encrypted.bin");
3500 
3501     check_nvs_part_gen_args(&emu2, NVS_DEFAULT_PART_NAME, 4, "testdata/sample_multipage_blob.bin", true, &cfg);
3502 
3503     childpid = fork();
3504     if (childpid == 0) {
3505         exit(execlp("bash", " bash",
3506                     "-c",
3507                     "rm -rf keys | \
3508                     rm -rf mfg_testdata | \
3509                     rm -rf testdata | \
3510                     rm -rf ../../../tools/mass_mfg/host_test",NULL));
3511     } else {
3512         CHECK(childpid > 0);
3513         waitpid(childpid, &status, 0);
3514         CHECK(WEXITSTATUS(status) == 0);
3515 
3516     }
3517 
3518 }
3519 #endif
3520 
3521 /* Add new tests above */
3522 /* This test has to be the final one */
3523 
3524 TEST_CASE("dump all performance data", "[nvs]")
3525 {
3526     std::cout << "====================" << std::endl << "Dumping benchmarks" << std::endl;
3527     std::cout << s_perf.str() << std::endl;
3528     std::cout << "====================" << std::endl;
3529 }
3530