-TEST_PROGRAM=fatfs_host
+TEST_PROGRAM=test_fatfs
+
+# Use wear levelling
+TEST_WL_COMPONENT=$(IDF_PATH)/components/wear_levelling
+TEST_WL_DIR=$(TEST_WL_COMPONENT)/test_wl_host
+TEST_WL_LIB=libtest_wl.a
+
+TEST_PARTITION_SIM_DIR=$(IDF_PATH)/components/spi_flash/sim
+TEST_PARTITION_SIM_LIB=libpartition_sim.a
all: $(TEST_PROGRAM)
SOURCE_FILES = \
- main.c \
+ main.cpp \
+ test_fatfs.cpp \
$(addprefix ../src/, \
diskio.c \
ff.c \
ffsystem.c \
- ffunicode.c \
- )
+ ffunicode.c \
+ diskio_spiflash.c \
+ ) \
+ $(addprefix ./stubs/, log/log.c)
INCLUDE_FLAGS = $(addprefix -I,\
../src \
- . \
- $(addprefix ./stubs/, \
- driver/include \
- freertos/include \
- sdmmc/include \
- ) \
- ../../esp32/include \
+ . \
+ $(addprefix ./stubs/, \
+ driver/include \
+ freertos/include \
+ sdmmc/include \
+ log/include \
+ ) \
+ ../../esp32/include \
+ $(TEST_PARTITION_SIM_DIR)/include \
+ $(TEST_WL_COMPONENT)/include \
+ ../../../tools/catch \
)
-CPPFLAGS += $(INCLUDE_FLAGS) -g
-CFLAGS += -fprofile-arcs -g
-CXXFLAGS += -std=c++11 -Wall -Werror -fprofile-arcs -g
-LDFLAGS += -lstdc++ -fprofile-arcs
+GCOV ?= gcov
+
+CPPFLAGS += $(INCLUDE_FLAGS) -D CONFIG_LOG_DEFAULT_LEVEL -g
+CFLAGS += -fprofile-arcs -ftest-coverage
+CXXFLAGS += -std=c++11 -Wall -Werror -fprofile-arcs -ftest-coverage
+LDFLAGS += -lstdc++ -fprofile-arcs -ftest-coverage
+
+OBJ_FILES = $(filter %.o, $(SOURCE_FILES:.cpp=.o) $(SOURCE_FILES:.c=.o))
+
+$(TEST_WL_DIR)/$(TEST_WL_LIB): force
+ $(MAKE) -C $(TEST_WL_DIR) lib
-OBJ_FILES = $(SOURCE_FILES:.c=.o)
-$(OBJ_FILES): %.o: %.c
+$(TEST_PARTITION_SIM_DIR)/$(TEST_PARTITION_SIM_LIB): force
+ $(MAKE) -C $(TEST_PARTITION_SIM_DIR) lib
-$(TEST_PROGRAM): $(OBJ_FILES)
- gcc $(LDFLAGS) -o $(TEST_PROGRAM) $(OBJ_FILES)
+force:
$(TEST_PROGRAM): $(OBJ_FILES) $(TEST_WL_DIR)/$(TEST_WL_LIB) $(TEST_PARTITION_SIM_DIR)/$(TEST_PARTITION_SIM_LIB)
g++ $(LDFLAGS) -o $(TEST_PROGRAM) $(OBJ_FILES) -L$(TEST_PARTITION_SIM_DIR) -l:$(TEST_PARTITION_SIM_LIB) -L$(TEST_WL_DIR) -l:$(TEST_WL_LIB)
-test: $(TEST_PROGRAM)
+run: $(TEST_PROGRAM)
./$(TEST_PROGRAM)
-clean:
+COVERAGE_FILES = $(OBJ_FILES:.o=.gc*) $(OBJ_FILES:.o=.gc*)
+
+$(COVERAGE_FILES): $(TEST_PROGRAM) lib
+
+coverage.info: $(COVERAGE_FILES)
+ find ../ -name "*.gcno" -exec $(GCOV) -r -pb {} +
+ lcov --capture --directory ../ --no-external --output-file coverage.info --gcov-tool $(GCOV)
+
+coverage_report: coverage.info
+ genhtml coverage.info --output-directory coverage_report
+ @echo "Coverage report is in coverage_report/index.html"
+
+clean:
rm -f $(OBJ_FILES) $(TEST_PROGRAM)
+ $(MAKE) -C $(TEST_WL_DIR) clean
+ $(MAKE) -C $(TEST_PARTITION_SIM_DIR) clean
+ rm -f $(COVERAGE_FILES) *.gcov
+ rm -rf coverage_report/
+ rm -f coverage.info
.PHONY: clean all
--- /dev/null
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
--- /dev/null
+# pragma once
+
+#define CONFIG_WL_SECTOR_SIZE 4096
--- /dev/null
+#pragma once
+
+#include <stdlib.h>
+
+#include "sdmmc_types.h"
+
--- /dev/null
+#pragma once
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef int sdmmc_card_t;
+
+#if defined(__cplusplus)
+}
+#endif
\ No newline at end of file
--- /dev/null
+#pragma once
+
+#include "projdefs.h"
+#include "semphr.h"
--- /dev/null
+#pragma once
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define pdTRUE 1
+
+#if defined(__cplusplus)
+}
+#endif
--- /dev/null
+#pragma once
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define vSemaphoreDelete( xSemaphore )
+#define xSemaphoreCreateMutex() ((void*)(1))
+#define xSemaphoreGive( xSemaphore )
+#define xSemaphoreTake( xSemaphore, xBlockTime ) pdTRUE
+
+typedef void* SemaphoreHandle_t;
+
+#if defined(__cplusplus)
+}
+#endif
--- /dev/null
+#pragma once
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "sdkconfig.h"
+
+#if defined(__cplusplus)
+extern "C" { // Make sure we have C-declarations in C++ programs
+#endif
+
+#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
+
+typedef enum {
+ ESP_LOG_NONE, /*!< No log output */
+ ESP_LOG_ERROR, /*!< Critical errors, software module can not recover on its own */
+ ESP_LOG_WARN, /*!< Error conditions from which recovery measures have been taken */
+ ESP_LOG_INFO, /*!< Information messages which describe normal flow of events */
+ ESP_LOG_DEBUG, /*!< Extra information which is not necessary for normal use (values, pointers, sizes, etc). */
+ ESP_LOG_VERBOSE /*!< Bigger chunks of debugging information, or frequent messages which can potentially flood the output. */
+} esp_log_level_t;
+
+#define LOG_COLOR_E
+#define LOG_COLOR_W
+#define LOG_COLOR_I
+#define LOG_COLOR_D
+#define LOG_COLOR_V
+#define LOG_RESET_COLOR
+
+uint32_t esp_log_timestamp(void);
+void esp_log_write(esp_log_level_t level, const char* tag, const char* format, ...) __attribute__ ((format (printf, 3, 4)));
+
+#define LOG_FORMAT(letter, format) LOG_COLOR_ ## letter #letter " (%d) %s: " format LOG_RESET_COLOR "\n"
+
+#define ESP_LOGE( tag, format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_ERROR) { esp_log_write(ESP_LOG_ERROR, tag, LOG_FORMAT(E, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
+
+#define ESP_LOGV( tag, format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_VERBOSE) { esp_log_write(ESP_LOG_VERBOSE, tag, LOG_FORMAT(V, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
+
+#define ESP_LOGD( tag, format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) { esp_log_write(ESP_LOG_DEBUG, tag, LOG_FORMAT(D, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
+
+#define ESP_LOGW( tag, format, ... ) if (LOG_LOCAL_LEVEL >= ESP_LOG_WARN) { esp_log_write(ESP_LOG_WARN, tag, LOG_FORMAT(W, format), esp_log_timestamp(), tag, ##__VA_ARGS__); }
+
+#if defined(__cplusplus)
+}
+#endif
\ No newline at end of file
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "esp_log.h"
+
+void esp_log_write(esp_log_level_t level,
+ const char *tag,
+ const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ vprintf(format, arg);
+ va_end(arg);
+}
+
+uint32_t esp_log_timestamp()
+{
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+#pragma once
+
+#include "esp_err.h"
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+
+#include "ff.h"
+#include "esp_partition.h"
+#include "wear_levelling.h"
+#include "diskio.h"
+#include "diskio_spiflash.h"
+
+#include "catch.hpp"
+
+TEST_CASE("create volume, open file, write and read back data", "[fatfs]")
+{
+ FRESULT fr_result;
+ BYTE pdrv;
+ FATFS fs;
+ FIL file;
+ UINT bw;
+
+ esp_err_t esp_result;
+
+ // Create a 4MB partition
+ uint32_t size = 0x00400000;
+ int flash_handle = esp_flash_create(size, 4096, 1);
+ esp_partition_t partition = esp_partition_create(size, 0, flash_handle);
+
+ // Mount wear-levelled partition
+ wl_handle_t wl_handle;
+ esp_result = wl_mount(&partition, &wl_handle);
+ REQUIRE(esp_result == ESP_OK);
+
+ // Get a physical drive
+ esp_result = ff_diskio_get_drive(&pdrv);
+ REQUIRE(esp_result == ESP_OK);
+
+ // Register physical drive as wear-levelled partition
+ esp_result = ff_diskio_register_wl_partition(pdrv, wl_handle);
+
+ // Create FAT volume on the entire disk
+ DWORD part_list[] = {100, 0, 0, 0};
+ BYTE work_area[FF_MAX_SS];
+
+ fr_result = f_fdisk(pdrv, part_list, work_area);
+ REQUIRE(fr_result == FR_OK);
+ fr_result = f_mkfs("", FM_ANY, 0, work_area, sizeof(work_area)); // Use default volume
+
+ // Mount the volume
+ fr_result = f_mount(&fs, "", 0);
+ REQUIRE(fr_result == FR_OK);
+
+ // Open, write and read data
+ fr_result = f_open(&file, "test.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE);
+ REQUIRE(fr_result == FR_OK);
+
+ const char data[] = "Hello, World!";
+ char *read = (char*) malloc(sizeof(data));
+
+ fr_result = f_write(&file, data, sizeof(data), &bw);
+ REQUIRE(fr_result == FR_OK);
+ REQUIRE(bw == sizeof(data));
+
+ // Move to beginning of file
+ fr_result = f_lseek(&file, 0);
+ REQUIRE(fr_result == FR_OK);
+
+ fr_result = f_read(&file, read, sizeof(data), &bw);
+ REQUIRE(fr_result == FR_OK);
+ REQUIRE(bw == sizeof(data));
+
+ REQUIRE(strcmp(data, read) == 0);
+
+ // Close file
+ fr_result = f_close(&file);
+ REQUIRE(fr_result == FR_OK);
+
+ // Unmount default volume
+ fr_result = f_mount(0, "", 0);
+ REQUIRE(fr_result == FR_OK);
+
+ esp_partition_delete(partition);
+
+ free(read);
+}
\ No newline at end of file
#include <stdbool.h>
+#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_spi_flash.h"
+#if defined(__cplusplus)
+extern "C" { // Make sure we have C-declarations in C++ programs
+#endif
+
/**
* @brief Partition type
* @note Keep this enum in sync with PartitionDefinition class gen_esp32part.py
void esp_partition_delete(esp_partition_t partition);
+#if defined(__cplusplus)
+}
+#endif
+
#include "esp_spi_flash.h"
#include "Flash_Emulator.h"
-#define PARTITIONS_MAX 8
+#define EMULATORS_MAX 8
-static Flash_Emulator* emulators[PARTITIONS_MAX];
+static Flash_Emulator* emulators[EMULATORS_MAX];
esp_partition_t esp_partition_create(uint32_t size, uint32_t start)
{
int handle = -1;
- for (int i = 0; i < PARTITIONS_MAX; i++) {
+ for (int i = 0; i < EMULATORS_MAX; i++) {
if (emulators[i] == NULL) {
emulators[i] = new Flash_Emulator(start + size, SPI_FLASH_SEC_SIZE, SPI_FLASH_WRITE_SIZE);
handle = i;