[FL-2627] Flipper applications: SDK, build and debug system (#1387)
* Added support for running applications from SD card (FAPs - Flipper Application Packages) * Added plugin_dist target for fbt to build FAPs * All apps of type FlipperAppType.EXTERNAL and FlipperAppType.PLUGIN are built as FAPs by default * Updated VSCode configuration for new fbt features - re-deploy stock configuration to use them * Added debugging support for FAPs with fbt debug & VSCode * Added public firmware API with automated versioning Co-authored-by: hedger <hedger@users.noreply.github.com> Co-authored-by: SG <who.just.the.doctor@gmail.com> Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
11
applications/debug/accessor/accessor.cpp
Normal file
11
applications/debug/accessor/accessor.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "accessor_app.h"
|
||||
|
||||
// app enter function
|
||||
extern "C" int32_t accessor_app(void* p) {
|
||||
UNUSED(p);
|
||||
AccessorApp* app = new AccessorApp();
|
||||
app->run();
|
||||
delete app;
|
||||
|
||||
return 255;
|
||||
}
|
140
applications/debug/accessor/accessor_app.cpp
Normal file
140
applications/debug/accessor/accessor_app.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
#include "accessor_app.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
void AccessorApp::run(void) {
|
||||
AccessorEvent event;
|
||||
bool consumed;
|
||||
bool exit = false;
|
||||
|
||||
wiegand.begin();
|
||||
onewire_host_start(onewire_host);
|
||||
|
||||
scenes[current_scene]->on_enter(this);
|
||||
|
||||
while(!exit) {
|
||||
view.receive_event(&event);
|
||||
|
||||
consumed = scenes[current_scene]->on_event(this, &event);
|
||||
|
||||
if(!consumed) {
|
||||
if(event.type == AccessorEvent::Type::Back) {
|
||||
exit = switch_to_previous_scene();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
scenes[current_scene]->on_exit(this);
|
||||
|
||||
wiegand.end();
|
||||
onewire_host_stop(onewire_host);
|
||||
}
|
||||
|
||||
AccessorApp::AccessorApp() {
|
||||
notification = static_cast<NotificationApp*>(furi_record_open(RECORD_NOTIFICATION));
|
||||
onewire_host = onewire_host_alloc();
|
||||
furi_hal_power_enable_otg();
|
||||
}
|
||||
|
||||
AccessorApp::~AccessorApp() {
|
||||
furi_hal_power_disable_otg();
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
onewire_host_free(onewire_host);
|
||||
}
|
||||
|
||||
AccessorAppViewManager* AccessorApp::get_view_manager() {
|
||||
return &view;
|
||||
}
|
||||
|
||||
void AccessorApp::switch_to_next_scene(Scene next_scene) {
|
||||
previous_scenes_list.push_front(current_scene);
|
||||
|
||||
if(next_scene != Scene::Exit) {
|
||||
scenes[current_scene]->on_exit(this);
|
||||
current_scene = next_scene;
|
||||
scenes[current_scene]->on_enter(this);
|
||||
}
|
||||
}
|
||||
|
||||
void AccessorApp::search_and_switch_to_previous_scene(std::initializer_list<Scene> scenes_list) {
|
||||
Scene previous_scene = Scene::Start;
|
||||
bool scene_found = false;
|
||||
|
||||
while(!scene_found) {
|
||||
previous_scene = get_previous_scene();
|
||||
for(Scene element : scenes_list) {
|
||||
if(previous_scene == element || previous_scene == Scene::Start) {
|
||||
scene_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scenes[current_scene]->on_exit(this);
|
||||
current_scene = previous_scene;
|
||||
scenes[current_scene]->on_enter(this);
|
||||
}
|
||||
|
||||
bool AccessorApp::switch_to_previous_scene(uint8_t count) {
|
||||
Scene previous_scene = Scene::Start;
|
||||
|
||||
for(uint8_t i = 0; i < count; i++) {
|
||||
previous_scene = get_previous_scene();
|
||||
if(previous_scene == Scene::Exit) break;
|
||||
}
|
||||
|
||||
if(previous_scene == Scene::Exit) {
|
||||
return true;
|
||||
} else {
|
||||
scenes[current_scene]->on_exit(this);
|
||||
current_scene = previous_scene;
|
||||
scenes[current_scene]->on_enter(this);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
AccessorApp::Scene AccessorApp::get_previous_scene() {
|
||||
Scene scene = previous_scenes_list.front();
|
||||
previous_scenes_list.pop_front();
|
||||
return scene;
|
||||
}
|
||||
|
||||
/***************************** NOTIFY *******************************/
|
||||
|
||||
void AccessorApp::notify_green_blink() {
|
||||
notification_message(notification, &sequence_blink_green_10);
|
||||
}
|
||||
|
||||
void AccessorApp::notify_success() {
|
||||
notification_message(notification, &sequence_success);
|
||||
}
|
||||
|
||||
/*************************** TEXT STORE *****************************/
|
||||
|
||||
char* AccessorApp::get_text_store() {
|
||||
return text_store;
|
||||
}
|
||||
|
||||
uint8_t AccessorApp::get_text_store_size() {
|
||||
return text_store_size;
|
||||
}
|
||||
|
||||
void AccessorApp::set_text_store(const char* text...) {
|
||||
va_list args;
|
||||
va_start(args, text);
|
||||
|
||||
vsnprintf(text_store, text_store_size, text, args);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/*************************** APP RESOURCES *****************************/
|
||||
|
||||
WIEGAND* AccessorApp::get_wiegand() {
|
||||
return &wiegand;
|
||||
}
|
||||
|
||||
OneWireHost* AccessorApp::get_one_wire() {
|
||||
return onewire_host;
|
||||
}
|
54
applications/debug/accessor/accessor_app.h
Normal file
54
applications/debug/accessor/accessor_app.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include "accessor_view_manager.h"
|
||||
#include "scene/accessor_scene_start.h"
|
||||
#include "helpers/wiegand.h"
|
||||
#include <one_wire/one_wire_host.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
class AccessorApp {
|
||||
public:
|
||||
void run(void);
|
||||
|
||||
AccessorApp();
|
||||
~AccessorApp();
|
||||
|
||||
enum class Scene : uint8_t {
|
||||
Exit,
|
||||
Start,
|
||||
};
|
||||
|
||||
AccessorAppViewManager* get_view_manager();
|
||||
void switch_to_next_scene(Scene index);
|
||||
void search_and_switch_to_previous_scene(std::initializer_list<Scene> scenes_list);
|
||||
bool switch_to_previous_scene(uint8_t count = 1);
|
||||
Scene get_previous_scene();
|
||||
|
||||
void notify_green_blink();
|
||||
void notify_success();
|
||||
|
||||
char* get_text_store();
|
||||
uint8_t get_text_store_size();
|
||||
void set_text_store(const char* text...);
|
||||
|
||||
WIEGAND* get_wiegand();
|
||||
OneWireHost* get_one_wire();
|
||||
|
||||
private:
|
||||
std::list<Scene> previous_scenes_list = {Scene::Exit};
|
||||
Scene current_scene = Scene::Start;
|
||||
AccessorAppViewManager view;
|
||||
|
||||
std::map<Scene, AccessorScene*> scenes = {
|
||||
{Scene::Start, new AccessorSceneStart()},
|
||||
};
|
||||
|
||||
static const uint8_t text_store_size = 128;
|
||||
char text_store[text_store_size + 1];
|
||||
|
||||
WIEGAND wiegand;
|
||||
OneWireHost* onewire_host;
|
||||
|
||||
NotificationApp* notification;
|
||||
};
|
19
applications/debug/accessor/accessor_event.h
Normal file
19
applications/debug/accessor/accessor_event.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
class AccessorEvent {
|
||||
public:
|
||||
// events enum
|
||||
enum class Type : uint8_t {
|
||||
Tick,
|
||||
Back,
|
||||
};
|
||||
|
||||
// payload
|
||||
union {
|
||||
uint32_t menu_index;
|
||||
} payload;
|
||||
|
||||
// event type
|
||||
Type type;
|
||||
};
|
80
applications/debug/accessor/accessor_view_manager.cpp
Normal file
80
applications/debug/accessor/accessor_view_manager.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "accessor_view_manager.h"
|
||||
#include "accessor_event.h"
|
||||
#include <callback-connector.h>
|
||||
|
||||
AccessorAppViewManager::AccessorAppViewManager() {
|
||||
event_queue = furi_message_queue_alloc(10, sizeof(AccessorEvent));
|
||||
|
||||
view_dispatcher = view_dispatcher_alloc();
|
||||
auto callback = cbc::obtain_connector(this, &AccessorAppViewManager::previous_view_callback);
|
||||
|
||||
// allocate views
|
||||
submenu = submenu_alloc();
|
||||
add_view(ViewType::Submenu, submenu_get_view(submenu));
|
||||
|
||||
popup = popup_alloc();
|
||||
add_view(ViewType::Popup, popup_get_view(popup));
|
||||
|
||||
gui = static_cast<Gui*>(furi_record_open(RECORD_GUI));
|
||||
view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// set previous view callback for all views
|
||||
view_set_previous_callback(submenu_get_view(submenu), callback);
|
||||
view_set_previous_callback(popup_get_view(popup), callback);
|
||||
}
|
||||
|
||||
AccessorAppViewManager::~AccessorAppViewManager() {
|
||||
// remove views
|
||||
view_dispatcher_remove_view(
|
||||
view_dispatcher, static_cast<uint32_t>(AccessorAppViewManager::ViewType::Submenu));
|
||||
view_dispatcher_remove_view(
|
||||
view_dispatcher, static_cast<uint32_t>(AccessorAppViewManager::ViewType::Popup));
|
||||
|
||||
// free view modules
|
||||
furi_record_close(RECORD_GUI);
|
||||
submenu_free(submenu);
|
||||
popup_free(popup);
|
||||
|
||||
// free dispatcher
|
||||
view_dispatcher_free(view_dispatcher);
|
||||
|
||||
// free event queue
|
||||
furi_message_queue_free(event_queue);
|
||||
}
|
||||
|
||||
void AccessorAppViewManager::switch_to(ViewType type) {
|
||||
view_dispatcher_switch_to_view(view_dispatcher, static_cast<uint32_t>(type));
|
||||
}
|
||||
|
||||
Submenu* AccessorAppViewManager::get_submenu() {
|
||||
return submenu;
|
||||
}
|
||||
|
||||
Popup* AccessorAppViewManager::get_popup() {
|
||||
return popup;
|
||||
}
|
||||
|
||||
void AccessorAppViewManager::receive_event(AccessorEvent* event) {
|
||||
if(furi_message_queue_get(event_queue, event, 100) != FuriStatusOk) {
|
||||
event->type = AccessorEvent::Type::Tick;
|
||||
}
|
||||
}
|
||||
|
||||
void AccessorAppViewManager::send_event(AccessorEvent* event) {
|
||||
FuriStatus result = furi_message_queue_put(event_queue, event, 0);
|
||||
furi_check(result == FuriStatusOk);
|
||||
}
|
||||
|
||||
uint32_t AccessorAppViewManager::previous_view_callback(void*) {
|
||||
if(event_queue != NULL) {
|
||||
AccessorEvent event;
|
||||
event.type = AccessorEvent::Type::Back;
|
||||
send_event(&event);
|
||||
}
|
||||
|
||||
return VIEW_IGNORE;
|
||||
}
|
||||
|
||||
void AccessorAppViewManager::add_view(ViewType view_type, View* view) {
|
||||
view_dispatcher_add_view(view_dispatcher, static_cast<uint32_t>(view_type), view);
|
||||
}
|
39
applications/debug/accessor/accessor_view_manager.h
Normal file
39
applications/debug/accessor/accessor_view_manager.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include <furi.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/popup.h>
|
||||
#include "accessor_event.h"
|
||||
|
||||
class AccessorAppViewManager {
|
||||
public:
|
||||
enum class ViewType : uint8_t {
|
||||
Submenu,
|
||||
Popup,
|
||||
Tune,
|
||||
};
|
||||
|
||||
FuriMessageQueue* event_queue;
|
||||
|
||||
AccessorAppViewManager();
|
||||
~AccessorAppViewManager();
|
||||
|
||||
void switch_to(ViewType type);
|
||||
|
||||
void receive_event(AccessorEvent* event);
|
||||
void send_event(AccessorEvent* event);
|
||||
|
||||
Submenu* get_submenu();
|
||||
Popup* get_popup();
|
||||
|
||||
private:
|
||||
ViewDispatcher* view_dispatcher;
|
||||
Gui* gui;
|
||||
|
||||
uint32_t previous_view_callback(void* context);
|
||||
void add_view(ViewType view_type, View* view);
|
||||
|
||||
// view elements
|
||||
Submenu* submenu;
|
||||
Popup* popup;
|
||||
};
|
11
applications/debug/accessor/application.fam
Normal file
11
applications/debug/accessor/application.fam
Normal file
@@ -0,0 +1,11 @@
|
||||
App(
|
||||
appid="accessor",
|
||||
name="Accessor",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="accessor_app",
|
||||
cdefines=["APP_ACCESSOR"],
|
||||
requires=["gui"],
|
||||
stack_size=4 * 1024,
|
||||
order=40,
|
||||
fap_category="Debug",
|
||||
)
|
224
applications/debug/accessor/helpers/wiegand.cpp
Normal file
224
applications/debug/accessor/helpers/wiegand.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
#include "wiegand.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
volatile unsigned long WIEGAND::_cardTempHigh = 0;
|
||||
volatile unsigned long WIEGAND::_cardTemp = 0;
|
||||
volatile unsigned long WIEGAND::_lastWiegand = 0;
|
||||
unsigned long WIEGAND::_code = 0;
|
||||
unsigned long WIEGAND::_codeHigh = 0;
|
||||
volatile int WIEGAND::_bitCount = 0;
|
||||
int WIEGAND::_wiegandType = 0;
|
||||
|
||||
constexpr uint32_t clocks_in_ms = 64 * 1000;
|
||||
const GpioPin* const pinD0 = &gpio_ext_pa4;
|
||||
const GpioPin* const pinD1 = &gpio_ext_pa7;
|
||||
|
||||
WIEGAND::WIEGAND() {
|
||||
}
|
||||
|
||||
unsigned long WIEGAND::getCode() {
|
||||
return _code;
|
||||
}
|
||||
|
||||
unsigned long WIEGAND::getCodeHigh() {
|
||||
return _codeHigh;
|
||||
}
|
||||
|
||||
int WIEGAND::getWiegandType() {
|
||||
return _wiegandType;
|
||||
}
|
||||
|
||||
bool WIEGAND::available() {
|
||||
bool ret;
|
||||
FURI_CRITICAL_ENTER();
|
||||
ret = DoWiegandConversion();
|
||||
FURI_CRITICAL_EXIT();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void input_isr_d0(void* _ctx) {
|
||||
WIEGAND* _this = static_cast<WIEGAND*>(_ctx);
|
||||
_this->ReadD0();
|
||||
}
|
||||
|
||||
static void input_isr_d1(void* _ctx) {
|
||||
WIEGAND* _this = static_cast<WIEGAND*>(_ctx);
|
||||
_this->ReadD1();
|
||||
}
|
||||
|
||||
void WIEGAND::begin() {
|
||||
_lastWiegand = 0;
|
||||
_cardTempHigh = 0;
|
||||
_cardTemp = 0;
|
||||
_code = 0;
|
||||
_wiegandType = 0;
|
||||
_bitCount = 0;
|
||||
|
||||
furi_hal_gpio_init_simple(pinD0, GpioModeInterruptFall); // Set D0 pin as input
|
||||
furi_hal_gpio_init_simple(pinD1, GpioModeInterruptFall); // Set D1 pin as input
|
||||
|
||||
furi_hal_gpio_add_int_callback(pinD0, input_isr_d0, this);
|
||||
furi_hal_gpio_add_int_callback(pinD1, input_isr_d1, this);
|
||||
}
|
||||
|
||||
void WIEGAND::end() {
|
||||
furi_hal_gpio_remove_int_callback(pinD0);
|
||||
furi_hal_gpio_remove_int_callback(pinD1);
|
||||
|
||||
furi_hal_gpio_init_simple(pinD0, GpioModeAnalog);
|
||||
furi_hal_gpio_init_simple(pinD1, GpioModeAnalog);
|
||||
}
|
||||
|
||||
void WIEGAND::ReadD0() {
|
||||
_bitCount++; // Increament bit count for Interrupt connected to D0
|
||||
if(_bitCount > 31) // If bit count more than 31, process high bits
|
||||
{
|
||||
_cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); // shift value to high bits
|
||||
_cardTempHigh <<= 1;
|
||||
_cardTemp <<= 1;
|
||||
} else {
|
||||
_cardTemp <<= 1; // D0 represent binary 0, so just left shift card data
|
||||
}
|
||||
_lastWiegand = DWT->CYCCNT; // Keep track of last wiegand bit received
|
||||
}
|
||||
|
||||
void WIEGAND::ReadD1() {
|
||||
_bitCount++; // Increment bit count for Interrupt connected to D1
|
||||
if(_bitCount > 31) // If bit count more than 31, process high bits
|
||||
{
|
||||
_cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); // shift value to high bits
|
||||
_cardTempHigh <<= 1;
|
||||
_cardTemp |= 1;
|
||||
_cardTemp <<= 1;
|
||||
} else {
|
||||
_cardTemp |= 1; // D1 represent binary 1, so OR card data with 1 then
|
||||
_cardTemp <<= 1; // left shift card data
|
||||
}
|
||||
_lastWiegand = DWT->CYCCNT; // Keep track of last wiegand bit received
|
||||
}
|
||||
|
||||
unsigned long WIEGAND::GetCardId(
|
||||
volatile unsigned long* codehigh,
|
||||
volatile unsigned long* codelow,
|
||||
char bitlength) {
|
||||
if(bitlength == 26) // EM tag
|
||||
return (*codelow & 0x1FFFFFE) >> 1;
|
||||
|
||||
if(bitlength == 24) return (*codelow & 0x7FFFFE) >> 1;
|
||||
|
||||
if(bitlength == 34) // Mifare
|
||||
{
|
||||
*codehigh = *codehigh & 0x03; // only need the 2 LSB of the codehigh
|
||||
*codehigh <<= 30; // shift 2 LSB to MSB
|
||||
*codelow >>= 1;
|
||||
return *codehigh | *codelow;
|
||||
}
|
||||
|
||||
if(bitlength == 32) {
|
||||
return (*codelow & 0x7FFFFFFE) >> 1;
|
||||
}
|
||||
|
||||
return *codelow; // EM tag or Mifare without parity bits
|
||||
}
|
||||
|
||||
char translateEnterEscapeKeyPress(char originalKeyPress) {
|
||||
switch(originalKeyPress) {
|
||||
case 0x0b: // 11 or * key
|
||||
return 0x0d; // 13 or ASCII ENTER
|
||||
|
||||
case 0x0a: // 10 or # key
|
||||
return 0x1b; // 27 or ASCII ESCAPE
|
||||
|
||||
default:
|
||||
return originalKeyPress;
|
||||
}
|
||||
}
|
||||
|
||||
bool WIEGAND::DoWiegandConversion() {
|
||||
unsigned long cardID;
|
||||
unsigned long sysTick = DWT->CYCCNT;
|
||||
|
||||
if((sysTick - _lastWiegand) >
|
||||
(25 * clocks_in_ms)) // if no more signal coming through after 25ms
|
||||
{
|
||||
if((_bitCount == 24) || (_bitCount == 26) || (_bitCount == 32) || (_bitCount == 34) ||
|
||||
(_bitCount == 37) || (_bitCount == 40) || (_bitCount == 8) ||
|
||||
(_bitCount ==
|
||||
4)) // bitCount for keypress=4 or 8, Wiegand 26=24 or 26, Wiegand 34=32 or 34
|
||||
{
|
||||
_codeHigh = 0;
|
||||
// shift right 1 bit to get back the real value - interrupt done 1 left shift in advance
|
||||
_cardTemp >>= 1;
|
||||
// bit count more than 32 bits, shift high bits right to make adjustment
|
||||
if(_bitCount > 32) _cardTempHigh >>= 1;
|
||||
|
||||
if(_bitCount == 8) // keypress wiegand with integrity
|
||||
{
|
||||
// 8-bit Wiegand keyboard data, high nibble is the "NOT" of low nibble
|
||||
// eg if key 1 pressed, data=E1 in binary 11100001 , high nibble=1110 , low nibble = 0001
|
||||
char highNibble = (_cardTemp & 0xf0) >> 4;
|
||||
char lowNibble = (_cardTemp & 0x0f);
|
||||
_wiegandType = _bitCount;
|
||||
_bitCount = 0;
|
||||
_cardTemp = 0;
|
||||
_cardTempHigh = 0;
|
||||
|
||||
if(lowNibble ==
|
||||
(~highNibble & 0x0f)) // check if low nibble matches the "NOT" of high nibble.
|
||||
{
|
||||
_code = (int)translateEnterEscapeKeyPress(lowNibble);
|
||||
return true;
|
||||
} else {
|
||||
_lastWiegand = sysTick;
|
||||
_bitCount = 0;
|
||||
_cardTemp = 0;
|
||||
_cardTempHigh = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Handle validation failure case!
|
||||
} else if(4 == _bitCount) {
|
||||
// 4-bit Wiegand codes have no data integrity check so we just
|
||||
// read the LOW nibble.
|
||||
_code = (int)translateEnterEscapeKeyPress(_cardTemp & 0x0000000F);
|
||||
|
||||
_wiegandType = _bitCount;
|
||||
_bitCount = 0;
|
||||
_cardTemp = 0;
|
||||
_cardTempHigh = 0;
|
||||
|
||||
return true;
|
||||
} else if(40 == _bitCount) {
|
||||
_cardTempHigh >>= 1;
|
||||
|
||||
_code = _cardTemp;
|
||||
_codeHigh = _cardTempHigh;
|
||||
|
||||
_wiegandType = _bitCount;
|
||||
_bitCount = 0;
|
||||
_cardTemp = 0;
|
||||
_cardTempHigh = 0;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// wiegand 26 or wiegand 34
|
||||
cardID = GetCardId(&_cardTempHigh, &_cardTemp, _bitCount);
|
||||
_wiegandType = _bitCount;
|
||||
_bitCount = 0;
|
||||
_cardTemp = 0;
|
||||
_cardTempHigh = 0;
|
||||
_code = cardID;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// well time over 25 ms and bitCount !=8 , !=26, !=34 , must be noise or nothing then.
|
||||
_lastWiegand = sysTick;
|
||||
_bitCount = 0;
|
||||
_cardTemp = 0;
|
||||
_cardTempHigh = 0;
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
return false;
|
||||
}
|
30
applications/debug/accessor/helpers/wiegand.h
Normal file
30
applications/debug/accessor/helpers/wiegand.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
class WIEGAND {
|
||||
public:
|
||||
WIEGAND();
|
||||
void begin();
|
||||
void end();
|
||||
bool available();
|
||||
unsigned long getCode();
|
||||
unsigned long getCodeHigh();
|
||||
int getWiegandType();
|
||||
|
||||
static void ReadD0();
|
||||
static void ReadD1();
|
||||
|
||||
private:
|
||||
static bool DoWiegandConversion();
|
||||
static unsigned long GetCardId(
|
||||
volatile unsigned long* codehigh,
|
||||
volatile unsigned long* codelow,
|
||||
char bitlength);
|
||||
|
||||
static volatile unsigned long _cardTempHigh;
|
||||
static volatile unsigned long _cardTemp;
|
||||
static volatile unsigned long _lastWiegand;
|
||||
static volatile int _bitCount;
|
||||
static int _wiegandType;
|
||||
static unsigned long _code;
|
||||
static unsigned long _codeHigh;
|
||||
};
|
13
applications/debug/accessor/scene/accessor_scene_generic.h
Normal file
13
applications/debug/accessor/scene/accessor_scene_generic.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "../accessor_app.h"
|
||||
|
||||
class AccessorApp;
|
||||
|
||||
class AccessorScene {
|
||||
public:
|
||||
virtual void on_enter(AccessorApp* app) = 0;
|
||||
virtual bool on_event(AccessorApp* app, AccessorEvent* event) = 0;
|
||||
virtual void on_exit(AccessorApp* app) = 0;
|
||||
|
||||
private:
|
||||
};
|
88
applications/debug/accessor/scene/accessor_scene_start.cpp
Normal file
88
applications/debug/accessor/scene/accessor_scene_start.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "../accessor_app.h"
|
||||
#include "../accessor_view_manager.h"
|
||||
#include "../accessor_event.h"
|
||||
#include <callback-connector.h>
|
||||
#include "accessor_scene_start.h"
|
||||
|
||||
void AccessorSceneStart::on_enter(AccessorApp* app) {
|
||||
AccessorAppViewManager* view_manager = app->get_view_manager();
|
||||
Popup* popup = view_manager->get_popup();
|
||||
|
||||
popup_set_header(popup, "Accessor App", 64, 16, AlignCenter, AlignBottom);
|
||||
app->set_text_store("[??????]");
|
||||
popup_set_text(popup, app->get_text_store(), 64, 22, AlignCenter, AlignTop);
|
||||
|
||||
view_manager->switch_to(AccessorAppViewManager::ViewType::Popup);
|
||||
}
|
||||
|
||||
bool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == AccessorEvent::Type::Tick) {
|
||||
WIEGAND* wiegand = app->get_wiegand();
|
||||
Popup* popup = app->get_view_manager()->get_popup();
|
||||
OneWireHost* onewire_host = app->get_one_wire();
|
||||
|
||||
uint8_t data[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
uint8_t type = 0;
|
||||
|
||||
if(wiegand->available()) {
|
||||
type = wiegand->getWiegandType();
|
||||
|
||||
for(uint8_t i = 0; i < 4; i++) {
|
||||
data[i] = wiegand->getCode() >> (i * 8);
|
||||
}
|
||||
|
||||
for(uint8_t i = 4; i < 8; i++) {
|
||||
data[i] = wiegand->getCodeHigh() >> ((i - 4) * 8);
|
||||
}
|
||||
} else {
|
||||
FURI_CRITICAL_ENTER();
|
||||
if(onewire_host_reset(onewire_host)) {
|
||||
type = 255;
|
||||
onewire_host_write(onewire_host, 0x33);
|
||||
for(uint8_t i = 0; i < 8; i++) {
|
||||
data[i] = onewire_host_read(onewire_host);
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < 7; i++) {
|
||||
data[i] = data[i + 1];
|
||||
}
|
||||
}
|
||||
FURI_CRITICAL_EXIT();
|
||||
}
|
||||
|
||||
if(type > 0) {
|
||||
if(type == 255) {
|
||||
app->set_text_store(
|
||||
"[%02X %02X %02X %02X %02X %02X DS]",
|
||||
data[5],
|
||||
data[4],
|
||||
data[3],
|
||||
data[2],
|
||||
data[1],
|
||||
data[0]);
|
||||
} else {
|
||||
app->set_text_store(
|
||||
"[%02X %02X %02X %02X %02X %02X W%u]",
|
||||
data[5],
|
||||
data[4],
|
||||
data[3],
|
||||
data[2],
|
||||
data[1],
|
||||
data[0],
|
||||
type);
|
||||
}
|
||||
popup_set_text(popup, app->get_text_store(), 64, 22, AlignCenter, AlignTop);
|
||||
app->notify_success();
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void AccessorSceneStart::on_exit(AccessorApp* app) {
|
||||
Popup* popup = app->get_view_manager()->get_popup();
|
||||
popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
|
||||
popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
|
||||
}
|
9
applications/debug/accessor/scene/accessor_scene_start.h
Normal file
9
applications/debug/accessor/scene/accessor_scene_start.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include "accessor_scene_generic.h"
|
||||
|
||||
class AccessorSceneStart : public AccessorScene {
|
||||
public:
|
||||
void on_enter(AccessorApp* app) final;
|
||||
bool on_event(AccessorApp* app, AccessorEvent* event) final;
|
||||
void on_exit(AccessorApp* app) final;
|
||||
};
|
16
applications/debug/application.fam
Normal file
16
applications/debug/application.fam
Normal file
@@ -0,0 +1,16 @@
|
||||
App(
|
||||
appid="debug_apps",
|
||||
name="Basic debug apps bundle",
|
||||
apptype=FlipperAppType.METAPACKAGE,
|
||||
provides=[
|
||||
"blink_test",
|
||||
"vibro_test",
|
||||
"keypad_test",
|
||||
"usb_test",
|
||||
"usb_mouse",
|
||||
"uart_echo",
|
||||
"display_test",
|
||||
"text_box_test",
|
||||
"file_browser_test",
|
||||
],
|
||||
)
|
14
applications/debug/battery_test_app/application.fam
Normal file
14
applications/debug/battery_test_app/application.fam
Normal file
@@ -0,0 +1,14 @@
|
||||
App(
|
||||
appid="battery_test",
|
||||
name="Battery Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="battery_test_app",
|
||||
cdefines=["APP_BATTERY_TEST"],
|
||||
requires=[
|
||||
"gui",
|
||||
"power",
|
||||
],
|
||||
stack_size=1 * 1024,
|
||||
order=130,
|
||||
fap_category="Debug",
|
||||
)
|
101
applications/debug/battery_test_app/battery_test_app.c
Normal file
101
applications/debug/battery_test_app/battery_test_app.c
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "battery_test_app.h"
|
||||
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
void battery_test_dialog_callback(DialogExResult result, void* context) {
|
||||
furi_assert(context);
|
||||
BatteryTestApp* app = context;
|
||||
if(result == DialogExResultLeft) {
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
} else if(result == DialogExResultRight) {
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, BatteryTestAppViewBatteryInfo);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t battery_test_exit_confirm_view() {
|
||||
return BatteryTestAppViewExitDialog;
|
||||
}
|
||||
|
||||
static void battery_test_battery_info_update_model(void* context) {
|
||||
BatteryTestApp* app = context;
|
||||
power_get_info(app->power, &app->info);
|
||||
BatteryInfoModel battery_info_data = {
|
||||
.vbus_voltage = app->info.voltage_vbus,
|
||||
.gauge_voltage = app->info.voltage_gauge,
|
||||
.gauge_current = app->info.current_gauge,
|
||||
.gauge_temperature = app->info.temperature_gauge,
|
||||
.charge = app->info.charge,
|
||||
.health = app->info.health,
|
||||
};
|
||||
battery_info_set_data(app->battery_info, &battery_info_data);
|
||||
notification_message(app->notifications, &sequence_display_backlight_on);
|
||||
}
|
||||
|
||||
BatteryTestApp* battery_test_alloc() {
|
||||
BatteryTestApp* app = malloc(sizeof(BatteryTestApp));
|
||||
|
||||
// Records
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->power = furi_record_open(RECORD_POWER);
|
||||
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
// View dispatcher
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
app->view_dispatcher, battery_test_battery_info_update_model, 500);
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// Views
|
||||
app->battery_info = battery_info_alloc();
|
||||
view_set_previous_callback(
|
||||
battery_info_get_view(app->battery_info), battery_test_exit_confirm_view);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
BatteryTestAppViewBatteryInfo,
|
||||
battery_info_get_view(app->battery_info));
|
||||
|
||||
app->dialog = dialog_ex_alloc();
|
||||
dialog_ex_set_header(app->dialog, "Close Battery Test?", 64, 12, AlignCenter, AlignTop);
|
||||
dialog_ex_set_left_button_text(app->dialog, "Exit");
|
||||
dialog_ex_set_right_button_text(app->dialog, "Stay");
|
||||
dialog_ex_set_result_callback(app->dialog, battery_test_dialog_callback);
|
||||
dialog_ex_set_context(app->dialog, app);
|
||||
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, BatteryTestAppViewExitDialog, dialog_ex_get_view(app->dialog));
|
||||
|
||||
battery_test_battery_info_update_model(app);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, BatteryTestAppViewBatteryInfo);
|
||||
return app;
|
||||
}
|
||||
|
||||
void battery_test_free(BatteryTestApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// Views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BatteryTestAppViewBatteryInfo);
|
||||
battery_info_free(app->battery_info);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BatteryTestAppViewExitDialog);
|
||||
dialog_ex_free(app->dialog);
|
||||
// View dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
// Records
|
||||
furi_record_close(RECORD_POWER);
|
||||
furi_record_close(RECORD_GUI);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t battery_test_app(void* p) {
|
||||
UNUSED(p);
|
||||
BatteryTestApp* app = battery_test_alloc();
|
||||
// Disable battery low level notification
|
||||
power_enable_low_battery_level_notification(app->power, false);
|
||||
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
power_enable_low_battery_level_notification(app->power, true);
|
||||
battery_test_free(app);
|
||||
return 0;
|
||||
}
|
25
applications/debug/battery_test_app/battery_test_app.h
Normal file
25
applications/debug/battery_test_app/battery_test_app.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <furi.h>
|
||||
#include <power/power_service/power.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <notification/notification.h>
|
||||
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
// FIXME
|
||||
#include "../settings/power_settings_app/views/battery_info.h"
|
||||
|
||||
typedef struct {
|
||||
Power* power;
|
||||
Gui* gui;
|
||||
NotificationApp* notifications;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
BatteryInfo* battery_info;
|
||||
DialogEx* dialog;
|
||||
PowerInfo info;
|
||||
} BatteryTestApp;
|
||||
|
||||
typedef enum {
|
||||
BatteryTestAppViewBatteryInfo,
|
||||
BatteryTestAppViewExitDialog,
|
||||
} BatteryTestAppView;
|
11
applications/debug/blink_test/application.fam
Normal file
11
applications/debug/blink_test/application.fam
Normal file
@@ -0,0 +1,11 @@
|
||||
App(
|
||||
appid="blink_test",
|
||||
name="Blink Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="blink_test_app",
|
||||
cdefines=["APP_BLINK"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=10,
|
||||
fap_category="Debug",
|
||||
)
|
126
applications/debug/blink_test/blink_test.c
Normal file
126
applications/debug/blink_test/blink_test.c
Normal file
@@ -0,0 +1,126 @@
|
||||
#include <core/common_defines.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
typedef enum {
|
||||
BlinkEventTypeTick,
|
||||
BlinkEventTypeInput,
|
||||
} BlinkEventType;
|
||||
|
||||
typedef struct {
|
||||
BlinkEventType type;
|
||||
InputEvent input;
|
||||
} BlinkEvent;
|
||||
|
||||
static const NotificationSequence blink_test_sequence_hw_blink_start_red = {
|
||||
&message_blink_start_10,
|
||||
&message_blink_set_color_red,
|
||||
&message_do_not_reset,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const NotificationSequence blink_test_sequence_hw_blink_green = {
|
||||
&message_blink_set_color_green,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const NotificationSequence blink_test_sequence_hw_blink_blue = {
|
||||
&message_blink_set_color_blue,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const NotificationSequence blink_test_sequence_hw_blink_stop = {
|
||||
&message_blink_stop,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const NotificationSequence* blink_test_colors[] = {
|
||||
&sequence_blink_red_100,
|
||||
&sequence_blink_green_100,
|
||||
&sequence_blink_blue_100,
|
||||
&sequence_blink_yellow_100,
|
||||
&sequence_blink_cyan_100,
|
||||
&sequence_blink_magenta_100,
|
||||
&sequence_blink_white_100,
|
||||
&blink_test_sequence_hw_blink_start_red,
|
||||
&blink_test_sequence_hw_blink_green,
|
||||
&blink_test_sequence_hw_blink_blue,
|
||||
&blink_test_sequence_hw_blink_stop,
|
||||
};
|
||||
|
||||
static void blink_test_update(void* ctx) {
|
||||
furi_assert(ctx);
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
BlinkEvent event = {.type = BlinkEventTypeTick};
|
||||
// It's OK to loose this event if system overloaded
|
||||
furi_message_queue_put(event_queue, &event, 0);
|
||||
}
|
||||
|
||||
static void blink_test_draw_callback(Canvas* canvas, void* ctx) {
|
||||
UNUSED(ctx);
|
||||
canvas_clear(canvas);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2, 10, "Blink application");
|
||||
}
|
||||
|
||||
static void blink_test_input_callback(InputEvent* input_event, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
|
||||
BlinkEvent event = {.type = BlinkEventTypeInput, .input = *input_event};
|
||||
furi_message_queue_put(event_queue, &event, FuriWaitForever);
|
||||
}
|
||||
|
||||
int32_t blink_test_app(void* p) {
|
||||
UNUSED(p);
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(BlinkEvent));
|
||||
|
||||
// Configure view port
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, blink_test_draw_callback, NULL);
|
||||
view_port_input_callback_set(view_port, blink_test_input_callback, event_queue);
|
||||
FuriTimer* timer = furi_timer_alloc(blink_test_update, FuriTimerTypePeriodic, event_queue);
|
||||
furi_timer_start(timer, furi_kernel_get_tick_frequency());
|
||||
|
||||
// Register view port in GUI
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
uint8_t state = 0;
|
||||
BlinkEvent event;
|
||||
|
||||
while(1) {
|
||||
furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk);
|
||||
if(event.type == BlinkEventTypeInput) {
|
||||
if((event.input.type == InputTypeShort) && (event.input.key == InputKeyBack)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
notification_message(notifications, blink_test_colors[state]);
|
||||
state++;
|
||||
if(state >= COUNT_OF(blink_test_colors)) {
|
||||
state = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notification_message(notifications, &blink_test_sequence_hw_blink_stop);
|
||||
|
||||
furi_timer_free(timer);
|
||||
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
return 0;
|
||||
}
|
18
applications/debug/bt_debug_app/application.fam
Normal file
18
applications/debug/bt_debug_app/application.fam
Normal file
@@ -0,0 +1,18 @@
|
||||
App(
|
||||
appid="bt_debug",
|
||||
name="Bluetooth Debug",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="bt_debug_app",
|
||||
cdefines=["SRV_BT"],
|
||||
requires=[
|
||||
"bt",
|
||||
"gui",
|
||||
"dialogs",
|
||||
],
|
||||
provides=[
|
||||
"bt_debug",
|
||||
],
|
||||
stack_size=1 * 1024,
|
||||
order=110,
|
||||
fap_category="Debug",
|
||||
)
|
119
applications/debug/bt_debug_app/bt_debug_app.c
Normal file
119
applications/debug/bt_debug_app/bt_debug_app.c
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "bt_debug_app.h"
|
||||
#include <furi_hal_bt.h>
|
||||
|
||||
#define TAG "BtDebugApp"
|
||||
|
||||
enum BtDebugSubmenuIndex {
|
||||
BtDebugSubmenuIndexCarrierTest,
|
||||
BtDebugSubmenuIndexPacketTest,
|
||||
};
|
||||
|
||||
void bt_debug_submenu_callback(void* context, uint32_t index) {
|
||||
furi_assert(context);
|
||||
BtDebugApp* app = context;
|
||||
if(index == BtDebugSubmenuIndexCarrierTest) {
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, BtDebugAppViewCarrierTest);
|
||||
} else if(index == BtDebugSubmenuIndexPacketTest) {
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, BtDebugAppViewPacketTest);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t bt_debug_exit(void* context) {
|
||||
UNUSED(context);
|
||||
return VIEW_NONE;
|
||||
}
|
||||
|
||||
uint32_t bt_debug_start_view(void* context) {
|
||||
UNUSED(context);
|
||||
return BtDebugAppViewSubmenu;
|
||||
}
|
||||
|
||||
BtDebugApp* bt_debug_app_alloc() {
|
||||
BtDebugApp* app = malloc(sizeof(BtDebugApp));
|
||||
|
||||
// Load settings
|
||||
bt_settings_load(&app->settings);
|
||||
|
||||
// Gui
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
// View dispatcher
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// Views
|
||||
app->submenu = submenu_alloc();
|
||||
submenu_add_item(
|
||||
app->submenu,
|
||||
"Carrier test",
|
||||
BtDebugSubmenuIndexCarrierTest,
|
||||
bt_debug_submenu_callback,
|
||||
app);
|
||||
submenu_add_item(
|
||||
app->submenu, "Packet test", BtDebugSubmenuIndexPacketTest, bt_debug_submenu_callback, app);
|
||||
view_set_previous_callback(submenu_get_view(app->submenu), bt_debug_exit);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, BtDebugAppViewSubmenu, submenu_get_view(app->submenu));
|
||||
app->bt_carrier_test = bt_carrier_test_alloc();
|
||||
view_set_previous_callback(
|
||||
bt_carrier_test_get_view(app->bt_carrier_test), bt_debug_start_view);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
BtDebugAppViewCarrierTest,
|
||||
bt_carrier_test_get_view(app->bt_carrier_test));
|
||||
app->bt_packet_test = bt_packet_test_alloc();
|
||||
view_set_previous_callback(bt_packet_test_get_view(app->bt_packet_test), bt_debug_start_view);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
BtDebugAppViewPacketTest,
|
||||
bt_packet_test_get_view(app->bt_packet_test));
|
||||
|
||||
// Switch to menu
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, BtDebugAppViewSubmenu);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void bt_debug_app_free(BtDebugApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// Free views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BtDebugAppViewSubmenu);
|
||||
submenu_free(app->submenu);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BtDebugAppViewCarrierTest);
|
||||
bt_carrier_test_free(app->bt_carrier_test);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, BtDebugAppViewPacketTest);
|
||||
bt_packet_test_free(app->bt_packet_test);
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
|
||||
// Close gui record
|
||||
furi_record_close(RECORD_GUI);
|
||||
app->gui = NULL;
|
||||
|
||||
// Free rest
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t bt_debug_app(void* p) {
|
||||
UNUSED(p);
|
||||
if(!furi_hal_bt_is_testing_supported()) {
|
||||
FURI_LOG_E(TAG, "Incorrect radio stack: radio testing fetures are absent.");
|
||||
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
dialog_message_show_storage_error(dialogs, "Incorrect\nRadioStack");
|
||||
return 255;
|
||||
}
|
||||
|
||||
BtDebugApp* app = bt_debug_app_alloc();
|
||||
// Stop advertising
|
||||
furi_hal_bt_stop_advertising();
|
||||
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
|
||||
// Restart advertising
|
||||
if(app->settings.enabled) {
|
||||
furi_hal_bt_start_advertising();
|
||||
}
|
||||
bt_debug_app_free(app);
|
||||
return 0;
|
||||
}
|
27
applications/debug/bt_debug_app/bt_debug_app.h
Normal file
27
applications/debug/bt_debug_app/bt_debug_app.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#include <gui/modules/submenu.h>
|
||||
#include "views/bt_carrier_test.h"
|
||||
#include "views/bt_packet_test.h"
|
||||
#include <bt/bt_settings.h>
|
||||
|
||||
typedef struct {
|
||||
BtSettings settings;
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
Submenu* submenu;
|
||||
BtCarrierTest* bt_carrier_test;
|
||||
BtPacketTest* bt_packet_test;
|
||||
} BtDebugApp;
|
||||
|
||||
typedef enum {
|
||||
BtDebugAppViewSubmenu,
|
||||
BtDebugAppViewCarrierTest,
|
||||
BtDebugAppViewPacketTest,
|
||||
} BtDebugAppView;
|
188
applications/debug/bt_debug_app/views/bt_carrier_test.c
Normal file
188
applications/debug/bt_debug_app/views/bt_carrier_test.c
Normal file
@@ -0,0 +1,188 @@
|
||||
#include "bt_carrier_test.h"
|
||||
#include "bt_test.h"
|
||||
#include "bt_test_types.h"
|
||||
#include "furi_hal_bt.h"
|
||||
|
||||
struct BtCarrierTest {
|
||||
BtTest* bt_test;
|
||||
BtTestParam* bt_param_channel;
|
||||
BtTestMode mode;
|
||||
BtTestChannel channel;
|
||||
BtTestPower power;
|
||||
FuriTimer* timer;
|
||||
};
|
||||
|
||||
static BtTestParamValue bt_param_mode[] = {
|
||||
{.value = BtTestModeRx, .str = "Rx"},
|
||||
{.value = BtTestModeTx, .str = "Tx"},
|
||||
{.value = BtTestModeTxHopping, .str = "Hopping Tx"},
|
||||
};
|
||||
|
||||
static BtTestParamValue bt_param_channel[] = {
|
||||
{.value = BtTestChannel2402, .str = "2402 MHz"},
|
||||
{.value = BtTestChannel2440, .str = "2440 MHz"},
|
||||
{.value = BtTestChannel2480, .str = "2480 MHz"},
|
||||
};
|
||||
|
||||
static BtTestParamValue bt_param_power[] = {
|
||||
{.value = BtPower0dB, .str = "0 dB"},
|
||||
{.value = BtPower2dB, .str = "2 dB"},
|
||||
{.value = BtPower4dB, .str = "4 dB"},
|
||||
{.value = BtPower6dB, .str = "6 dB"},
|
||||
};
|
||||
|
||||
static void bt_carrier_test_start(BtCarrierTest* bt_carrier_test) {
|
||||
furi_assert(bt_carrier_test);
|
||||
if(bt_carrier_test->mode == BtTestModeRx) {
|
||||
furi_hal_bt_start_packet_rx(bt_carrier_test->channel, 1);
|
||||
furi_timer_start(bt_carrier_test->timer, furi_kernel_get_tick_frequency() / 4);
|
||||
} else if(bt_carrier_test->mode == BtTestModeTxHopping) {
|
||||
furi_hal_bt_start_tone_tx(bt_carrier_test->channel, bt_carrier_test->power);
|
||||
furi_timer_start(bt_carrier_test->timer, furi_kernel_get_tick_frequency() * 2);
|
||||
} else if(bt_carrier_test->mode == BtTestModeTx) {
|
||||
furi_hal_bt_start_tone_tx(bt_carrier_test->channel, bt_carrier_test->power);
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_carrier_test_switch_channel(BtCarrierTest* bt_carrier_test) {
|
||||
furi_assert(bt_carrier_test);
|
||||
furi_hal_bt_stop_tone_tx();
|
||||
uint8_t channel_i = 0;
|
||||
if(bt_carrier_test->channel == BtTestChannel2402) {
|
||||
bt_carrier_test->channel = BtTestChannel2440;
|
||||
channel_i = 1;
|
||||
} else if(bt_carrier_test->channel == BtTestChannel2440) {
|
||||
bt_carrier_test->channel = BtTestChannel2480;
|
||||
channel_i = 2;
|
||||
} else if(bt_carrier_test->channel == BtTestChannel2480) {
|
||||
bt_carrier_test->channel = BtTestChannel2402;
|
||||
channel_i = 0;
|
||||
}
|
||||
furi_hal_bt_start_tone_tx(bt_carrier_test->channel, bt_carrier_test->power);
|
||||
bt_test_set_current_value_index(bt_carrier_test->bt_param_channel, channel_i);
|
||||
bt_test_set_current_value_text(
|
||||
bt_carrier_test->bt_param_channel, bt_param_channel[channel_i].str);
|
||||
}
|
||||
|
||||
static void bt_carrier_test_stop(BtCarrierTest* bt_carrier_test) {
|
||||
furi_assert(bt_carrier_test);
|
||||
if(bt_carrier_test->mode == BtTestModeTxHopping) {
|
||||
furi_hal_bt_stop_tone_tx();
|
||||
furi_timer_stop(bt_carrier_test->timer);
|
||||
} else if(bt_carrier_test->mode == BtTestModeTx) {
|
||||
furi_hal_bt_stop_tone_tx();
|
||||
} else if(bt_carrier_test->mode == BtTestModeRx) {
|
||||
furi_hal_bt_stop_packet_test();
|
||||
furi_timer_stop(bt_carrier_test->timer);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t bt_carrier_test_param_changed(BtTestParam* param, BtTestParamValue* param_val) {
|
||||
furi_assert(param);
|
||||
uint8_t index = bt_test_get_current_value_index(param);
|
||||
bt_test_set_current_value_text(param, param_val[index].str);
|
||||
return param_val[index].value;
|
||||
}
|
||||
|
||||
static void bt_carrier_test_mode_changed(BtTestParam* param) {
|
||||
BtCarrierTest* bt_carrier_test = bt_test_get_context(param);
|
||||
bt_carrier_test_stop(bt_carrier_test);
|
||||
bt_carrier_test->mode = bt_carrier_test_param_changed(param, bt_param_mode);
|
||||
}
|
||||
|
||||
static void bt_carrier_test_channel_changed(BtTestParam* param) {
|
||||
BtCarrierTest* bt_carrier_test = bt_test_get_context(param);
|
||||
bt_carrier_test_stop(bt_carrier_test);
|
||||
bt_carrier_test->channel = bt_carrier_test_param_changed(param, bt_param_channel);
|
||||
}
|
||||
|
||||
static void bt_carrier_test_param_channel(BtTestParam* param) {
|
||||
BtCarrierTest* bt_carrier_test = bt_test_get_context(param);
|
||||
bt_carrier_test_stop(bt_carrier_test);
|
||||
bt_carrier_test->power = bt_carrier_test_param_changed(param, bt_param_power);
|
||||
}
|
||||
|
||||
static void bt_carrier_test_change_state_callback(BtTestState state, void* context) {
|
||||
furi_assert(context);
|
||||
BtCarrierTest* bt_carrier_test = context;
|
||||
furi_hal_bt_stop_tone_tx();
|
||||
if(state == BtTestStateStarted) {
|
||||
bt_carrier_test_start(bt_carrier_test);
|
||||
} else if(state == BtTestStateStopped) {
|
||||
bt_carrier_test_stop(bt_carrier_test);
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_carrier_test_exit_callback(void* context) {
|
||||
furi_assert(context);
|
||||
BtCarrierTest* bt_carrier_test = context;
|
||||
bt_carrier_test_stop(bt_carrier_test);
|
||||
}
|
||||
|
||||
static void bt_test_carrier_timer_callback(void* context) {
|
||||
furi_assert(context);
|
||||
BtCarrierTest* bt_carrier_test = context;
|
||||
if(bt_carrier_test->mode == BtTestModeRx) {
|
||||
bt_test_set_rssi(bt_carrier_test->bt_test, furi_hal_bt_get_rssi());
|
||||
} else if(bt_carrier_test->mode == BtTestModeTxHopping) {
|
||||
bt_carrier_test_switch_channel(bt_carrier_test);
|
||||
}
|
||||
}
|
||||
|
||||
BtCarrierTest* bt_carrier_test_alloc() {
|
||||
BtCarrierTest* bt_carrier_test = malloc(sizeof(BtCarrierTest));
|
||||
bt_carrier_test->bt_test = bt_test_alloc();
|
||||
bt_test_set_context(bt_carrier_test->bt_test, bt_carrier_test);
|
||||
bt_test_set_change_state_callback(
|
||||
bt_carrier_test->bt_test, bt_carrier_test_change_state_callback);
|
||||
bt_test_set_back_callback(bt_carrier_test->bt_test, bt_carrier_test_exit_callback);
|
||||
|
||||
BtTestParam* param;
|
||||
param = bt_test_param_add(
|
||||
bt_carrier_test->bt_test,
|
||||
"Mode",
|
||||
COUNT_OF(bt_param_mode),
|
||||
bt_carrier_test_mode_changed,
|
||||
bt_carrier_test);
|
||||
bt_test_set_current_value_index(param, 0);
|
||||
bt_test_set_current_value_text(param, bt_param_mode[0].str);
|
||||
bt_carrier_test->mode = BtTestModeRx;
|
||||
|
||||
param = bt_test_param_add(
|
||||
bt_carrier_test->bt_test,
|
||||
"Channel",
|
||||
COUNT_OF(bt_param_channel),
|
||||
bt_carrier_test_channel_changed,
|
||||
bt_carrier_test);
|
||||
bt_test_set_current_value_index(param, 0);
|
||||
bt_test_set_current_value_text(param, bt_param_channel[0].str);
|
||||
bt_carrier_test->channel = BtTestChannel2402;
|
||||
bt_carrier_test->bt_param_channel = param;
|
||||
|
||||
param = bt_test_param_add(
|
||||
bt_carrier_test->bt_test,
|
||||
"Power",
|
||||
COUNT_OF(bt_param_power),
|
||||
bt_carrier_test_param_channel,
|
||||
bt_carrier_test);
|
||||
bt_test_set_current_value_index(param, 0);
|
||||
bt_test_set_current_value_text(param, bt_param_power[0].str);
|
||||
bt_carrier_test->power = BtPower0dB;
|
||||
|
||||
bt_carrier_test->timer =
|
||||
furi_timer_alloc(bt_test_carrier_timer_callback, FuriTimerTypePeriodic, bt_carrier_test);
|
||||
|
||||
return bt_carrier_test;
|
||||
}
|
||||
|
||||
void bt_carrier_test_free(BtCarrierTest* bt_carrier_test) {
|
||||
furi_assert(bt_carrier_test);
|
||||
bt_test_free(bt_carrier_test->bt_test);
|
||||
furi_timer_free(bt_carrier_test->timer);
|
||||
free(bt_carrier_test);
|
||||
}
|
||||
|
||||
View* bt_carrier_test_get_view(BtCarrierTest* bt_carrier_test) {
|
||||
furi_assert(bt_carrier_test);
|
||||
return bt_test_get_view(bt_carrier_test->bt_test);
|
||||
}
|
10
applications/debug/bt_debug_app/views/bt_carrier_test.h
Normal file
10
applications/debug/bt_debug_app/views/bt_carrier_test.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include <gui/view.h>
|
||||
|
||||
typedef struct BtCarrierTest BtCarrierTest;
|
||||
|
||||
BtCarrierTest* bt_carrier_test_alloc();
|
||||
|
||||
void bt_carrier_test_free(BtCarrierTest* bt_carrier_test);
|
||||
|
||||
View* bt_carrier_test_get_view(BtCarrierTest* bt_carrier_test);
|
155
applications/debug/bt_debug_app/views/bt_packet_test.c
Normal file
155
applications/debug/bt_debug_app/views/bt_packet_test.c
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "bt_packet_test.h"
|
||||
#include "bt_test.h"
|
||||
#include "bt_test_types.h"
|
||||
#include "furi_hal_bt.h"
|
||||
|
||||
struct BtPacketTest {
|
||||
BtTest* bt_test;
|
||||
BtTestMode mode;
|
||||
BtTestChannel channel;
|
||||
BtTestDataRate data_rate;
|
||||
FuriTimer* timer;
|
||||
};
|
||||
|
||||
static BtTestParamValue bt_param_mode[] = {
|
||||
{.value = BtTestModeRx, .str = "Rx"},
|
||||
{.value = BtTestModeTx, .str = "Tx"},
|
||||
};
|
||||
|
||||
static BtTestParamValue bt_param_channel[] = {
|
||||
{.value = BtTestChannel2402, .str = "2402 MHz"},
|
||||
{.value = BtTestChannel2440, .str = "2440 MHz"},
|
||||
{.value = BtTestChannel2480, .str = "2480 MHz"},
|
||||
};
|
||||
|
||||
static BtTestParamValue bt_param_data_rate[] = {
|
||||
{.value = BtDataRate1M, .str = "1 Mbps"},
|
||||
{.value = BtDataRate2M, .str = "2 Mbps"},
|
||||
};
|
||||
|
||||
static void bt_packet_test_start(BtPacketTest* bt_packet_test) {
|
||||
furi_assert(bt_packet_test);
|
||||
if(bt_packet_test->mode == BtTestModeRx) {
|
||||
furi_hal_bt_start_packet_rx(bt_packet_test->channel, bt_packet_test->data_rate);
|
||||
furi_timer_start(bt_packet_test->timer, furi_kernel_get_tick_frequency() / 4);
|
||||
} else if(bt_packet_test->mode == BtTestModeTx) {
|
||||
furi_hal_bt_start_packet_tx(bt_packet_test->channel, 1, bt_packet_test->data_rate);
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_packet_test_stop(BtPacketTest* bt_packet_test) {
|
||||
furi_assert(bt_packet_test);
|
||||
if(bt_packet_test->mode == BtTestModeTx) {
|
||||
furi_hal_bt_stop_packet_test();
|
||||
bt_test_set_packets_tx(bt_packet_test->bt_test, furi_hal_bt_get_transmitted_packets());
|
||||
} else if(bt_packet_test->mode == BtTestModeRx) {
|
||||
bt_test_set_packets_rx(bt_packet_test->bt_test, furi_hal_bt_stop_packet_test());
|
||||
furi_timer_stop(bt_packet_test->timer);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t bt_packet_test_param_changed(BtTestParam* param, BtTestParamValue* param_val) {
|
||||
furi_assert(param);
|
||||
uint8_t index = bt_test_get_current_value_index(param);
|
||||
bt_test_set_current_value_text(param, param_val[index].str);
|
||||
return param_val[index].value;
|
||||
}
|
||||
|
||||
static void bt_packet_test_mode_changed(BtTestParam* param) {
|
||||
BtPacketTest* bt_packet_test = bt_test_get_context(param);
|
||||
bt_packet_test_stop(bt_packet_test);
|
||||
bt_packet_test->mode = bt_packet_test_param_changed(param, bt_param_mode);
|
||||
}
|
||||
|
||||
static void bt_packet_test_channel_changed(BtTestParam* param) {
|
||||
BtPacketTest* bt_packet_test = bt_test_get_context(param);
|
||||
bt_packet_test_stop(bt_packet_test);
|
||||
bt_packet_test->channel = bt_packet_test_param_changed(param, bt_param_channel);
|
||||
}
|
||||
|
||||
static void bt_packet_test_param_channel(BtTestParam* param) {
|
||||
BtPacketTest* bt_packet_test = bt_test_get_context(param);
|
||||
bt_packet_test_stop(bt_packet_test);
|
||||
bt_packet_test->data_rate = bt_packet_test_param_changed(param, bt_param_data_rate);
|
||||
}
|
||||
|
||||
static void bt_packet_test_change_state_callback(BtTestState state, void* context) {
|
||||
furi_assert(context);
|
||||
BtPacketTest* bt_packet_test = context;
|
||||
if(state == BtTestStateStarted) {
|
||||
bt_packet_test_start(bt_packet_test);
|
||||
} else if(state == BtTestStateStopped) {
|
||||
bt_packet_test_stop(bt_packet_test);
|
||||
}
|
||||
}
|
||||
|
||||
static void bt_packet_test_exit_callback(void* context) {
|
||||
furi_assert(context);
|
||||
BtPacketTest* bt_packet_test = context;
|
||||
bt_packet_test_stop(bt_packet_test);
|
||||
}
|
||||
|
||||
static void bt_test_packet_timer_callback(void* context) {
|
||||
furi_assert(context);
|
||||
BtPacketTest* bt_packet_test = context;
|
||||
if(bt_packet_test->mode == BtTestModeRx) {
|
||||
bt_test_set_rssi(bt_packet_test->bt_test, furi_hal_bt_get_rssi());
|
||||
}
|
||||
}
|
||||
|
||||
BtPacketTest* bt_packet_test_alloc() {
|
||||
BtPacketTest* bt_packet_test = malloc(sizeof(BtPacketTest));
|
||||
bt_packet_test->bt_test = bt_test_alloc();
|
||||
bt_test_set_context(bt_packet_test->bt_test, bt_packet_test);
|
||||
bt_test_set_change_state_callback(
|
||||
bt_packet_test->bt_test, bt_packet_test_change_state_callback);
|
||||
bt_test_set_back_callback(bt_packet_test->bt_test, bt_packet_test_exit_callback);
|
||||
|
||||
BtTestParam* param;
|
||||
param = bt_test_param_add(
|
||||
bt_packet_test->bt_test,
|
||||
"Mode",
|
||||
COUNT_OF(bt_param_mode),
|
||||
bt_packet_test_mode_changed,
|
||||
bt_packet_test);
|
||||
bt_test_set_current_value_index(param, 0);
|
||||
bt_test_set_current_value_text(param, bt_param_mode[0].str);
|
||||
bt_packet_test->mode = BtTestModeRx;
|
||||
|
||||
param = bt_test_param_add(
|
||||
bt_packet_test->bt_test,
|
||||
"Channel",
|
||||
COUNT_OF(bt_param_channel),
|
||||
bt_packet_test_channel_changed,
|
||||
bt_packet_test);
|
||||
bt_test_set_current_value_index(param, 0);
|
||||
bt_test_set_current_value_text(param, bt_param_channel[0].str);
|
||||
bt_packet_test->channel = BtTestChannel2402;
|
||||
|
||||
param = bt_test_param_add(
|
||||
bt_packet_test->bt_test,
|
||||
"Data rate",
|
||||
COUNT_OF(bt_param_data_rate),
|
||||
bt_packet_test_param_channel,
|
||||
bt_packet_test);
|
||||
bt_test_set_current_value_index(param, 0);
|
||||
bt_test_set_current_value_text(param, bt_param_data_rate[0].str);
|
||||
bt_packet_test->data_rate = BtDataRate1M;
|
||||
|
||||
bt_packet_test->timer =
|
||||
furi_timer_alloc(bt_test_packet_timer_callback, FuriTimerTypePeriodic, bt_packet_test);
|
||||
|
||||
return bt_packet_test;
|
||||
}
|
||||
|
||||
void bt_packet_test_free(BtPacketTest* bt_packet_test) {
|
||||
furi_assert(bt_packet_test);
|
||||
bt_test_free(bt_packet_test->bt_test);
|
||||
furi_timer_free(bt_packet_test->timer);
|
||||
free(bt_packet_test);
|
||||
}
|
||||
|
||||
View* bt_packet_test_get_view(BtPacketTest* bt_packet_test) {
|
||||
furi_assert(bt_packet_test);
|
||||
return bt_test_get_view(bt_packet_test->bt_test);
|
||||
}
|
10
applications/debug/bt_debug_app/views/bt_packet_test.h
Normal file
10
applications/debug/bt_debug_app/views/bt_packet_test.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include <gui/view.h>
|
||||
|
||||
typedef struct BtPacketTest BtPacketTest;
|
||||
|
||||
BtPacketTest* bt_packet_test_alloc();
|
||||
|
||||
void bt_packet_test_free(BtPacketTest* bt_packet_test);
|
||||
|
||||
View* bt_packet_test_get_view(BtPacketTest* bt_packet_test);
|
422
applications/debug/bt_debug_app/views/bt_test.c
Normal file
422
applications/debug/bt_debug_app/views/bt_test.c
Normal file
@@ -0,0 +1,422 @@
|
||||
#include "bt_test.h"
|
||||
|
||||
#include <gui/canvas.h>
|
||||
#include <gui/elements.h>
|
||||
#include <m-array.h>
|
||||
#include <m-string.h>
|
||||
#include <furi.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct BtTestParam {
|
||||
const char* label;
|
||||
uint8_t current_value_index;
|
||||
string_t current_value_text;
|
||||
uint8_t values_count;
|
||||
BtTestParamChangeCallback change_callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
ARRAY_DEF(BtTestParamArray, BtTestParam, M_POD_OPLIST);
|
||||
|
||||
struct BtTest {
|
||||
View* view;
|
||||
BtTestChangeStateCallback change_state_callback;
|
||||
BtTestBackCallback back_callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
BtTestState state;
|
||||
BtTestParamArray_t params;
|
||||
uint8_t position;
|
||||
uint8_t window_position;
|
||||
const char* message;
|
||||
float rssi;
|
||||
uint32_t packets_num_rx;
|
||||
uint32_t packets_num_tx;
|
||||
} BtTestModel;
|
||||
|
||||
#define BT_TEST_START_MESSAGE "Ok - Start"
|
||||
#define BT_TEST_STOP_MESSAGE "Ok - Stop"
|
||||
|
||||
static void bt_test_process_up(BtTest* bt_test);
|
||||
static void bt_test_process_down(BtTest* bt_test);
|
||||
static void bt_test_process_left(BtTest* bt_test);
|
||||
static void bt_test_process_right(BtTest* bt_test);
|
||||
static void bt_test_process_ok(BtTest* bt_test);
|
||||
static void bt_test_process_back(BtTest* bt_test);
|
||||
|
||||
static void bt_test_draw_callback(Canvas* canvas, void* _model) {
|
||||
BtTestModel* model = _model;
|
||||
char info_str[32];
|
||||
|
||||
const uint8_t param_height = 16;
|
||||
const uint8_t param_width = 123;
|
||||
|
||||
canvas_clear(canvas);
|
||||
|
||||
uint8_t position = 0;
|
||||
BtTestParamArray_it_t it;
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
for(BtTestParamArray_it(it, model->params); !BtTestParamArray_end_p(it);
|
||||
BtTestParamArray_next(it)) {
|
||||
uint8_t param_position = position - model->window_position;
|
||||
uint8_t params_on_screen = 3;
|
||||
uint8_t y_offset = 0;
|
||||
|
||||
if(param_position < params_on_screen) {
|
||||
const BtTestParam* param = BtTestParamArray_cref(it);
|
||||
uint8_t param_y = y_offset + (param_position * param_height);
|
||||
uint8_t param_text_y = param_y + param_height - 4;
|
||||
|
||||
if(position == model->position) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
elements_slightly_rounded_box(
|
||||
canvas, 0, param_y + 1, param_width, param_height - 2);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
canvas_draw_str(canvas, 6, param_text_y, param->label);
|
||||
|
||||
if(param->current_value_index > 0) {
|
||||
canvas_draw_str(canvas, 50, param_text_y, "<");
|
||||
}
|
||||
|
||||
canvas_draw_str(canvas, 61, param_text_y, string_get_cstr(param->current_value_text));
|
||||
|
||||
if(param->current_value_index < (param->values_count - 1)) {
|
||||
canvas_draw_str(canvas, 113, param_text_y, ">");
|
||||
}
|
||||
}
|
||||
|
||||
position++;
|
||||
}
|
||||
|
||||
elements_scrollbar(canvas, model->position, BtTestParamArray_size(model->params));
|
||||
canvas_draw_str(canvas, 6, 60, model->message);
|
||||
if(model->state == BtTestStateStarted) {
|
||||
if(model->rssi != 0.0f) {
|
||||
snprintf(info_str, sizeof(info_str), "RSSI:%3.1f dB", (double)model->rssi);
|
||||
canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str);
|
||||
}
|
||||
} else if(model->state == BtTestStateStopped) {
|
||||
if(model->packets_num_rx) {
|
||||
snprintf(info_str, sizeof(info_str), "%ld pack rcv", model->packets_num_rx);
|
||||
canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str);
|
||||
} else if(model->packets_num_tx) {
|
||||
snprintf(info_str, sizeof(info_str), "%ld pack sent", model->packets_num_tx);
|
||||
canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool bt_test_input_callback(InputEvent* event, void* context) {
|
||||
BtTest* bt_test = context;
|
||||
furi_assert(bt_test);
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == InputTypeShort) {
|
||||
switch(event->key) {
|
||||
case InputKeyUp:
|
||||
consumed = true;
|
||||
bt_test_process_up(bt_test);
|
||||
break;
|
||||
case InputKeyDown:
|
||||
consumed = true;
|
||||
bt_test_process_down(bt_test);
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
consumed = true;
|
||||
bt_test_process_left(bt_test);
|
||||
break;
|
||||
case InputKeyRight:
|
||||
consumed = true;
|
||||
bt_test_process_right(bt_test);
|
||||
break;
|
||||
case InputKeyOk:
|
||||
consumed = true;
|
||||
bt_test_process_ok(bt_test);
|
||||
break;
|
||||
case InputKeyBack:
|
||||
consumed = false;
|
||||
bt_test_process_back(bt_test);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void bt_test_process_up(BtTest* bt_test) {
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
uint8_t params_on_screen = 3;
|
||||
if(model->position > 0) {
|
||||
model->position--;
|
||||
if(((model->position - model->window_position) < 1) &&
|
||||
model->window_position > 0) {
|
||||
model->window_position--;
|
||||
}
|
||||
} else {
|
||||
model->position = BtTestParamArray_size(model->params) - 1;
|
||||
if(model->position > (params_on_screen - 1)) {
|
||||
model->window_position = model->position - (params_on_screen - 1);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void bt_test_process_down(BtTest* bt_test) {
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
uint8_t params_on_screen = 3;
|
||||
if(model->position < (BtTestParamArray_size(model->params) - 1)) {
|
||||
model->position++;
|
||||
if((model->position - model->window_position) > (params_on_screen - 2) &&
|
||||
model->window_position <
|
||||
(BtTestParamArray_size(model->params) - params_on_screen)) {
|
||||
model->window_position++;
|
||||
}
|
||||
} else {
|
||||
model->position = 0;
|
||||
model->window_position = 0;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
BtTestParam* bt_test_get_selected_param(BtTestModel* model) {
|
||||
BtTestParam* param = NULL;
|
||||
|
||||
BtTestParamArray_it_t it;
|
||||
uint8_t position = 0;
|
||||
for(BtTestParamArray_it(it, model->params); !BtTestParamArray_end_p(it);
|
||||
BtTestParamArray_next(it)) {
|
||||
if(position == model->position) {
|
||||
break;
|
||||
}
|
||||
position++;
|
||||
}
|
||||
|
||||
param = BtTestParamArray_ref(it);
|
||||
|
||||
furi_assert(param);
|
||||
return param;
|
||||
}
|
||||
|
||||
void bt_test_process_left(BtTest* bt_test) {
|
||||
BtTestParam* param;
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
param = bt_test_get_selected_param(model);
|
||||
if(param->current_value_index > 0) {
|
||||
param->current_value_index--;
|
||||
if(param->change_callback) {
|
||||
model->state = BtTestStateStopped;
|
||||
model->message = BT_TEST_START_MESSAGE;
|
||||
model->rssi = 0.0f;
|
||||
model->packets_num_rx = 0;
|
||||
model->packets_num_tx = 0;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if(param->change_callback) {
|
||||
param->change_callback(param);
|
||||
}
|
||||
}
|
||||
|
||||
void bt_test_process_right(BtTest* bt_test) {
|
||||
BtTestParam* param;
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
param = bt_test_get_selected_param(model);
|
||||
if(param->current_value_index < (param->values_count - 1)) {
|
||||
param->current_value_index++;
|
||||
if(param->change_callback) {
|
||||
model->state = BtTestStateStopped;
|
||||
model->message = BT_TEST_START_MESSAGE;
|
||||
model->rssi = 0.0f;
|
||||
model->packets_num_rx = 0;
|
||||
model->packets_num_tx = 0;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if(param->change_callback) {
|
||||
param->change_callback(param);
|
||||
}
|
||||
}
|
||||
|
||||
void bt_test_process_ok(BtTest* bt_test) {
|
||||
BtTestState state;
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
if(model->state == BtTestStateStarted) {
|
||||
model->state = BtTestStateStopped;
|
||||
model->message = BT_TEST_START_MESSAGE;
|
||||
model->rssi = 0.0f;
|
||||
model->packets_num_rx = 0;
|
||||
model->packets_num_tx = 0;
|
||||
} else if(model->state == BtTestStateStopped) {
|
||||
model->state = BtTestStateStarted;
|
||||
model->message = BT_TEST_STOP_MESSAGE;
|
||||
}
|
||||
state = model->state;
|
||||
return true;
|
||||
});
|
||||
if(bt_test->change_state_callback) {
|
||||
bt_test->change_state_callback(state, bt_test->context);
|
||||
}
|
||||
}
|
||||
|
||||
void bt_test_process_back(BtTest* bt_test) {
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
model->state = BtTestStateStopped;
|
||||
model->rssi = 0.0f;
|
||||
model->packets_num_rx = 0;
|
||||
model->packets_num_tx = 0;
|
||||
return false;
|
||||
});
|
||||
if(bt_test->back_callback) {
|
||||
bt_test->back_callback(bt_test->context);
|
||||
}
|
||||
}
|
||||
|
||||
BtTest* bt_test_alloc() {
|
||||
BtTest* bt_test = malloc(sizeof(BtTest));
|
||||
bt_test->view = view_alloc();
|
||||
view_set_context(bt_test->view, bt_test);
|
||||
view_allocate_model(bt_test->view, ViewModelTypeLocking, sizeof(BtTestModel));
|
||||
view_set_draw_callback(bt_test->view, bt_test_draw_callback);
|
||||
view_set_input_callback(bt_test->view, bt_test_input_callback);
|
||||
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
model->state = BtTestStateStopped;
|
||||
model->message = "Ok - Start";
|
||||
BtTestParamArray_init(model->params);
|
||||
model->position = 0;
|
||||
model->window_position = 0;
|
||||
model->rssi = 0.0f;
|
||||
model->packets_num_tx = 0;
|
||||
model->packets_num_rx = 0;
|
||||
return true;
|
||||
});
|
||||
|
||||
return bt_test;
|
||||
}
|
||||
|
||||
void bt_test_free(BtTest* bt_test) {
|
||||
furi_assert(bt_test);
|
||||
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
BtTestParamArray_it_t it;
|
||||
for(BtTestParamArray_it(it, model->params); !BtTestParamArray_end_p(it);
|
||||
BtTestParamArray_next(it)) {
|
||||
string_clear(BtTestParamArray_ref(it)->current_value_text);
|
||||
}
|
||||
BtTestParamArray_clear(model->params);
|
||||
return false;
|
||||
});
|
||||
view_free(bt_test->view);
|
||||
free(bt_test);
|
||||
}
|
||||
|
||||
View* bt_test_get_view(BtTest* bt_test) {
|
||||
furi_assert(bt_test);
|
||||
return bt_test->view;
|
||||
}
|
||||
|
||||
BtTestParam* bt_test_param_add(
|
||||
BtTest* bt_test,
|
||||
const char* label,
|
||||
uint8_t values_count,
|
||||
BtTestParamChangeCallback change_callback,
|
||||
void* context) {
|
||||
BtTestParam* param = NULL;
|
||||
furi_assert(label);
|
||||
furi_assert(bt_test);
|
||||
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
param = BtTestParamArray_push_new(model->params);
|
||||
param->label = label;
|
||||
param->values_count = values_count;
|
||||
param->change_callback = change_callback;
|
||||
param->context = context;
|
||||
param->current_value_index = 0;
|
||||
string_init(param->current_value_text);
|
||||
return true;
|
||||
});
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
void bt_test_set_rssi(BtTest* bt_test, float rssi) {
|
||||
furi_assert(bt_test);
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
model->rssi = rssi;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void bt_test_set_packets_tx(BtTest* bt_test, uint32_t packets_num) {
|
||||
furi_assert(bt_test);
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
model->packets_num_tx = packets_num;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void bt_test_set_packets_rx(BtTest* bt_test, uint32_t packets_num) {
|
||||
furi_assert(bt_test);
|
||||
with_view_model(
|
||||
bt_test->view, (BtTestModel * model) {
|
||||
model->packets_num_rx = packets_num;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void bt_test_set_change_state_callback(BtTest* bt_test, BtTestChangeStateCallback callback) {
|
||||
furi_assert(bt_test);
|
||||
furi_assert(callback);
|
||||
bt_test->change_state_callback = callback;
|
||||
}
|
||||
|
||||
void bt_test_set_back_callback(BtTest* bt_test, BtTestBackCallback callback) {
|
||||
furi_assert(bt_test);
|
||||
furi_assert(callback);
|
||||
bt_test->back_callback = callback;
|
||||
}
|
||||
|
||||
void bt_test_set_context(BtTest* bt_test, void* context) {
|
||||
furi_assert(bt_test);
|
||||
bt_test->context = context;
|
||||
}
|
||||
|
||||
void bt_test_set_current_value_index(BtTestParam* param, uint8_t current_value_index) {
|
||||
param->current_value_index = current_value_index;
|
||||
}
|
||||
|
||||
void bt_test_set_current_value_text(BtTestParam* param, const char* current_value_text) {
|
||||
string_set_str(param->current_value_text, current_value_text);
|
||||
}
|
||||
|
||||
uint8_t bt_test_get_current_value_index(BtTestParam* param) {
|
||||
return param->current_value_index;
|
||||
}
|
||||
|
||||
void* bt_test_get_context(BtTestParam* param) {
|
||||
return param->context;
|
||||
}
|
46
applications/debug/bt_debug_app/views/bt_test.h
Normal file
46
applications/debug/bt_debug_app/views/bt_test.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include <gui/view.h>
|
||||
|
||||
typedef enum {
|
||||
BtTestStateStarted,
|
||||
BtTestStateStopped,
|
||||
} BtTestState;
|
||||
|
||||
typedef struct BtTest BtTest;
|
||||
typedef void (*BtTestChangeStateCallback)(BtTestState state, void* context);
|
||||
typedef void (*BtTestBackCallback)(void* context);
|
||||
typedef struct BtTestParam BtTestParam;
|
||||
typedef void (*BtTestParamChangeCallback)(BtTestParam* param);
|
||||
|
||||
BtTest* bt_test_alloc();
|
||||
|
||||
void bt_test_free(BtTest* bt_test);
|
||||
|
||||
View* bt_test_get_view(BtTest* bt_test);
|
||||
|
||||
BtTestParam* bt_test_param_add(
|
||||
BtTest* bt_test,
|
||||
const char* label,
|
||||
uint8_t values_count,
|
||||
BtTestParamChangeCallback change_callback,
|
||||
void* context);
|
||||
|
||||
void bt_test_set_change_state_callback(BtTest* bt_test, BtTestChangeStateCallback callback);
|
||||
|
||||
void bt_test_set_back_callback(BtTest* bt_test, BtTestBackCallback callback);
|
||||
|
||||
void bt_test_set_context(BtTest* bt_test, void* context);
|
||||
|
||||
void bt_test_set_rssi(BtTest* bt_test, float rssi);
|
||||
|
||||
void bt_test_set_packets_tx(BtTest* bt_test, uint32_t packets_num);
|
||||
|
||||
void bt_test_set_packets_rx(BtTest* bt_test, uint32_t packets_num);
|
||||
|
||||
void bt_test_set_current_value_index(BtTestParam* param, uint8_t current_value_index);
|
||||
|
||||
void bt_test_set_current_value_text(BtTestParam* param, const char* current_value_text);
|
||||
|
||||
uint8_t bt_test_get_current_value_index(BtTestParam* param);
|
||||
|
||||
void* bt_test_get_context(BtTestParam* param);
|
30
applications/debug/bt_debug_app/views/bt_test_types.h
Normal file
30
applications/debug/bt_debug_app/views/bt_test_types.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
BtTestModeRx,
|
||||
BtTestModeTx,
|
||||
BtTestModeTxHopping,
|
||||
} BtTestMode;
|
||||
|
||||
typedef enum {
|
||||
BtTestChannel2402 = 0,
|
||||
BtTestChannel2440 = 19,
|
||||
BtTestChannel2480 = 39,
|
||||
} BtTestChannel;
|
||||
|
||||
typedef enum {
|
||||
BtPower0dB = 0x19,
|
||||
BtPower2dB = 0x1B,
|
||||
BtPower4dB = 0x1D,
|
||||
BtPower6dB = 0x1F,
|
||||
} BtTestPower;
|
||||
|
||||
typedef enum {
|
||||
BtDataRate1M = 1,
|
||||
BtDataRate2M = 2,
|
||||
} BtTestDataRate;
|
||||
|
||||
typedef struct {
|
||||
uint32_t value;
|
||||
const char* str;
|
||||
} BtTestParamValue;
|
11
applications/debug/display_test/application.fam
Normal file
11
applications/debug/display_test/application.fam
Normal file
@@ -0,0 +1,11 @@
|
||||
App(
|
||||
appid="display_test",
|
||||
name="Display Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="display_test_app",
|
||||
cdefines=["APP_DISPLAY_TEST"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=120,
|
||||
fap_category="Debug",
|
||||
)
|
232
applications/debug/display_test/display_test.c
Normal file
232
applications/debug/display_test/display_test.c
Normal file
@@ -0,0 +1,232 @@
|
||||
#include "display_test.h"
|
||||
|
||||
#include <furi_hal.h>
|
||||
#include <furi.h>
|
||||
|
||||
// Need access to u8g2
|
||||
#include <gui/gui_i.h>
|
||||
#include <gui/canvas_i.h>
|
||||
#include <u8g2_glue.h>
|
||||
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
|
||||
#include "view_display_test.h"
|
||||
|
||||
#define TAG "DisplayTest"
|
||||
|
||||
typedef struct {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
ViewDisplayTest* view_display_test;
|
||||
VariableItemList* variable_item_list;
|
||||
Submenu* submenu;
|
||||
|
||||
bool config_bias;
|
||||
uint8_t config_contrast;
|
||||
uint8_t config_regulation_ratio;
|
||||
} DisplayTest;
|
||||
|
||||
typedef enum {
|
||||
DisplayTestViewSubmenu,
|
||||
DisplayTestViewConfigure,
|
||||
DisplayTestViewDisplayTest,
|
||||
} DisplayTestView;
|
||||
|
||||
const bool config_bias_value[] = {
|
||||
true,
|
||||
false,
|
||||
};
|
||||
const char* const config_bias_text[] = {
|
||||
"1/7",
|
||||
"1/9",
|
||||
};
|
||||
|
||||
const uint8_t config_regulation_ratio_value[] = {
|
||||
0b000,
|
||||
0b001,
|
||||
0b010,
|
||||
0b011,
|
||||
0b100,
|
||||
0b101,
|
||||
0b110,
|
||||
0b111,
|
||||
};
|
||||
const char* const config_regulation_ratio_text[] = {
|
||||
"3.0",
|
||||
"3.5",
|
||||
"4.0",
|
||||
"4.5",
|
||||
"5.0",
|
||||
"5.5",
|
||||
"6.0",
|
||||
"6.5",
|
||||
};
|
||||
|
||||
static void display_test_submenu_callback(void* context, uint32_t index) {
|
||||
DisplayTest* instance = (DisplayTest*)context;
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, index);
|
||||
}
|
||||
|
||||
static uint32_t display_test_previous_callback(void* context) {
|
||||
UNUSED(context);
|
||||
return DisplayTestViewSubmenu;
|
||||
}
|
||||
|
||||
static uint32_t display_test_exit_callback(void* context) {
|
||||
UNUSED(context);
|
||||
return VIEW_NONE;
|
||||
}
|
||||
|
||||
static void display_test_reload_config(DisplayTest* instance) {
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"contrast: %d, regulation_ratio: %d, bias: %d",
|
||||
instance->config_contrast,
|
||||
instance->config_regulation_ratio,
|
||||
instance->config_bias);
|
||||
u8x8_d_st756x_init(
|
||||
&instance->gui->canvas->fb.u8x8,
|
||||
instance->config_contrast,
|
||||
instance->config_regulation_ratio,
|
||||
instance->config_bias);
|
||||
gui_update(instance->gui);
|
||||
}
|
||||
|
||||
static void display_config_set_bias(VariableItem* item) {
|
||||
DisplayTest* instance = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
variable_item_set_current_value_text(item, config_bias_text[index]);
|
||||
instance->config_bias = config_bias_value[index];
|
||||
display_test_reload_config(instance);
|
||||
}
|
||||
|
||||
static void display_config_set_regulation_ratio(VariableItem* item) {
|
||||
DisplayTest* instance = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
variable_item_set_current_value_text(item, config_regulation_ratio_text[index]);
|
||||
instance->config_regulation_ratio = config_regulation_ratio_value[index];
|
||||
display_test_reload_config(instance);
|
||||
}
|
||||
|
||||
static void display_config_set_contrast(VariableItem* item) {
|
||||
DisplayTest* instance = variable_item_get_context(item);
|
||||
uint8_t index = variable_item_get_current_value_index(item);
|
||||
string_t temp;
|
||||
string_init(temp);
|
||||
string_cat_printf(temp, "%d", index);
|
||||
variable_item_set_current_value_text(item, string_get_cstr(temp));
|
||||
string_clear(temp);
|
||||
instance->config_contrast = index;
|
||||
display_test_reload_config(instance);
|
||||
}
|
||||
|
||||
DisplayTest* display_test_alloc() {
|
||||
DisplayTest* instance = malloc(sizeof(DisplayTest));
|
||||
|
||||
View* view = NULL;
|
||||
|
||||
instance->gui = furi_record_open(RECORD_GUI);
|
||||
instance->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(instance->view_dispatcher);
|
||||
view_dispatcher_attach_to_gui(
|
||||
instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// Test
|
||||
instance->view_display_test = view_display_test_alloc();
|
||||
view = view_display_test_get_view(instance->view_display_test);
|
||||
view_set_previous_callback(view, display_test_previous_callback);
|
||||
view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewDisplayTest, view);
|
||||
|
||||
// Configure
|
||||
instance->variable_item_list = variable_item_list_alloc();
|
||||
view = variable_item_list_get_view(instance->variable_item_list);
|
||||
view_set_previous_callback(view, display_test_previous_callback);
|
||||
view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewConfigure, view);
|
||||
|
||||
// Configurtion items
|
||||
VariableItem* item;
|
||||
instance->config_bias = false;
|
||||
instance->config_contrast = 32;
|
||||
instance->config_regulation_ratio = 0b101;
|
||||
// Bias
|
||||
item = variable_item_list_add(
|
||||
instance->variable_item_list,
|
||||
"Bias:",
|
||||
COUNT_OF(config_bias_value),
|
||||
display_config_set_bias,
|
||||
instance);
|
||||
variable_item_set_current_value_index(item, 1);
|
||||
variable_item_set_current_value_text(item, config_bias_text[1]);
|
||||
// Regulation Ratio
|
||||
item = variable_item_list_add(
|
||||
instance->variable_item_list,
|
||||
"Reg Ratio:",
|
||||
COUNT_OF(config_regulation_ratio_value),
|
||||
display_config_set_regulation_ratio,
|
||||
instance);
|
||||
variable_item_set_current_value_index(item, 5);
|
||||
variable_item_set_current_value_text(item, config_regulation_ratio_text[5]);
|
||||
// Contrast
|
||||
item = variable_item_list_add(
|
||||
instance->variable_item_list, "Contrast:", 64, display_config_set_contrast, instance);
|
||||
variable_item_set_current_value_index(item, 32);
|
||||
variable_item_set_current_value_text(item, "32");
|
||||
|
||||
// Menu
|
||||
instance->submenu = submenu_alloc();
|
||||
view = submenu_get_view(instance->submenu);
|
||||
view_set_previous_callback(view, display_test_exit_callback);
|
||||
view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewSubmenu, view);
|
||||
submenu_add_item(
|
||||
instance->submenu,
|
||||
"Test",
|
||||
DisplayTestViewDisplayTest,
|
||||
display_test_submenu_callback,
|
||||
instance);
|
||||
submenu_add_item(
|
||||
instance->submenu,
|
||||
"Configure",
|
||||
DisplayTestViewConfigure,
|
||||
display_test_submenu_callback,
|
||||
instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void display_test_free(DisplayTest* instance) {
|
||||
view_dispatcher_remove_view(instance->view_dispatcher, DisplayTestViewSubmenu);
|
||||
submenu_free(instance->submenu);
|
||||
|
||||
view_dispatcher_remove_view(instance->view_dispatcher, DisplayTestViewConfigure);
|
||||
variable_item_list_free(instance->variable_item_list);
|
||||
|
||||
view_dispatcher_remove_view(instance->view_dispatcher, DisplayTestViewDisplayTest);
|
||||
view_display_test_free(instance->view_display_test);
|
||||
|
||||
view_dispatcher_free(instance->view_dispatcher);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
int32_t display_test_run(DisplayTest* instance) {
|
||||
UNUSED(instance);
|
||||
view_dispatcher_switch_to_view(instance->view_dispatcher, DisplayTestViewSubmenu);
|
||||
view_dispatcher_run(instance->view_dispatcher);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t display_test_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
DisplayTest* instance = display_test_alloc();
|
||||
|
||||
int32_t ret = display_test_run(instance);
|
||||
|
||||
display_test_free(instance);
|
||||
|
||||
return ret;
|
||||
}
|
1
applications/debug/display_test/display_test.h
Normal file
1
applications/debug/display_test/display_test.h
Normal file
@@ -0,0 +1 @@
|
||||
#pragma once
|
186
applications/debug/display_test/view_display_test.c
Normal file
186
applications/debug/display_test/view_display_test.c
Normal file
@@ -0,0 +1,186 @@
|
||||
#include "view_display_test.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t test;
|
||||
uint32_t size;
|
||||
uint32_t counter;
|
||||
bool flip_flop;
|
||||
} ViewDisplayTestModel;
|
||||
|
||||
struct ViewDisplayTest {
|
||||
View* view;
|
||||
FuriTimer* timer;
|
||||
};
|
||||
|
||||
static void view_display_test_draw_callback_intro(Canvas* canvas, void* _model) {
|
||||
UNUSED(_model);
|
||||
canvas_draw_str(canvas, 12, 24, "Use < and > to switch tests");
|
||||
canvas_draw_str(canvas, 12, 36, "Use ^ and v to switch size");
|
||||
canvas_draw_str(canvas, 32, 48, "Use (o) to flip");
|
||||
}
|
||||
|
||||
static void view_display_test_draw_callback_fill(Canvas* canvas, void* _model) {
|
||||
ViewDisplayTestModel* model = _model;
|
||||
if(model->flip_flop) {
|
||||
uint8_t width = canvas_width(canvas);
|
||||
uint8_t height = canvas_height(canvas);
|
||||
canvas_draw_box(canvas, 0, 0, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
static void view_display_test_draw_callback_hstripe(Canvas* canvas, void* _model) {
|
||||
ViewDisplayTestModel* model = _model;
|
||||
uint8_t block = 1 + model->size;
|
||||
uint8_t width = canvas_width(canvas);
|
||||
uint8_t height = canvas_height(canvas);
|
||||
|
||||
for(uint8_t y = model->flip_flop * block; y < height; y += 2 * block) {
|
||||
canvas_draw_box(canvas, 0, y, width, block);
|
||||
}
|
||||
}
|
||||
|
||||
static void view_display_test_draw_callback_vstripe(Canvas* canvas, void* _model) {
|
||||
ViewDisplayTestModel* model = _model;
|
||||
uint8_t block = 1 + model->size;
|
||||
uint8_t width = canvas_width(canvas);
|
||||
uint8_t height = canvas_height(canvas);
|
||||
|
||||
for(uint8_t x = model->flip_flop * block; x < width; x += 2 * block) {
|
||||
canvas_draw_box(canvas, x, 0, block, height);
|
||||
}
|
||||
}
|
||||
|
||||
static void view_display_test_draw_callback_check(Canvas* canvas, void* _model) {
|
||||
ViewDisplayTestModel* model = _model;
|
||||
uint8_t block = 1 + model->size;
|
||||
uint8_t width = canvas_width(canvas);
|
||||
uint8_t height = canvas_height(canvas);
|
||||
|
||||
bool flip_flop = model->flip_flop;
|
||||
for(uint8_t x = 0; x < width; x += block) {
|
||||
bool last_flip_flop = flip_flop;
|
||||
for(uint8_t y = 0; y < height; y += block) {
|
||||
if(flip_flop) {
|
||||
canvas_draw_box(canvas, x, y, block, block);
|
||||
}
|
||||
flip_flop = !flip_flop;
|
||||
}
|
||||
if(last_flip_flop == flip_flop) {
|
||||
flip_flop = !flip_flop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void view_display_test_draw_callback_move(Canvas* canvas, void* _model) {
|
||||
ViewDisplayTestModel* model = _model;
|
||||
uint8_t block = 1 + model->size;
|
||||
uint8_t width = canvas_width(canvas) - block;
|
||||
uint8_t height = canvas_height(canvas) - block;
|
||||
|
||||
uint8_t x = model->counter % width;
|
||||
if((model->counter / width) % 2) {
|
||||
x = width - x;
|
||||
}
|
||||
|
||||
uint8_t y = model->counter % height;
|
||||
if((model->counter / height) % 2) {
|
||||
y = height - y;
|
||||
}
|
||||
|
||||
canvas_draw_box(canvas, x, y, block, block);
|
||||
}
|
||||
|
||||
const ViewDrawCallback view_display_test_tests[] = {
|
||||
view_display_test_draw_callback_intro,
|
||||
view_display_test_draw_callback_fill,
|
||||
view_display_test_draw_callback_hstripe,
|
||||
view_display_test_draw_callback_vstripe,
|
||||
view_display_test_draw_callback_check,
|
||||
view_display_test_draw_callback_move,
|
||||
};
|
||||
|
||||
static void view_display_test_draw_callback(Canvas* canvas, void* _model) {
|
||||
ViewDisplayTestModel* model = _model;
|
||||
view_display_test_tests[model->test](canvas, _model);
|
||||
}
|
||||
|
||||
static bool view_display_test_input_callback(InputEvent* event, void* context) {
|
||||
ViewDisplayTest* instance = context;
|
||||
|
||||
bool consumed = false;
|
||||
if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
|
||||
with_view_model(
|
||||
instance->view, (ViewDisplayTestModel * model) {
|
||||
if(event->key == InputKeyLeft && model->test > 0) {
|
||||
model->test--;
|
||||
consumed = true;
|
||||
} else if(
|
||||
event->key == InputKeyRight &&
|
||||
model->test < (COUNT_OF(view_display_test_tests) - 1)) {
|
||||
model->test++;
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyDown && model->size > 0) {
|
||||
model->size--;
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyUp && model->size < 24) {
|
||||
model->size++;
|
||||
consumed = true;
|
||||
} else if(event->key == InputKeyOk) {
|
||||
model->flip_flop = !model->flip_flop;
|
||||
consumed = true;
|
||||
}
|
||||
return consumed;
|
||||
});
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
static void view_display_test_enter(void* context) {
|
||||
ViewDisplayTest* instance = context;
|
||||
furi_timer_start(instance->timer, furi_kernel_get_tick_frequency() / 32);
|
||||
}
|
||||
|
||||
static void view_display_test_exit(void* context) {
|
||||
ViewDisplayTest* instance = context;
|
||||
furi_timer_stop(instance->timer);
|
||||
}
|
||||
|
||||
static void view_display_test_timer_callback(void* context) {
|
||||
ViewDisplayTest* instance = context;
|
||||
with_view_model(
|
||||
instance->view, (ViewDisplayTestModel * model) {
|
||||
model->counter++;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
ViewDisplayTest* view_display_test_alloc() {
|
||||
ViewDisplayTest* instance = malloc(sizeof(ViewDisplayTest));
|
||||
|
||||
instance->view = view_alloc();
|
||||
view_set_context(instance->view, instance);
|
||||
view_allocate_model(instance->view, ViewModelTypeLockFree, sizeof(ViewDisplayTestModel));
|
||||
view_set_draw_callback(instance->view, view_display_test_draw_callback);
|
||||
view_set_input_callback(instance->view, view_display_test_input_callback);
|
||||
view_set_enter_callback(instance->view, view_display_test_enter);
|
||||
view_set_exit_callback(instance->view, view_display_test_exit);
|
||||
|
||||
instance->timer =
|
||||
furi_timer_alloc(view_display_test_timer_callback, FuriTimerTypePeriodic, instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void view_display_test_free(ViewDisplayTest* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
furi_timer_free(instance->timer);
|
||||
view_free(instance->view);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
View* view_display_test_get_view(ViewDisplayTest* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->view;
|
||||
}
|
12
applications/debug/display_test/view_display_test.h
Normal file
12
applications/debug/display_test/view_display_test.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <gui/view.h>
|
||||
|
||||
typedef struct ViewDisplayTest ViewDisplayTest;
|
||||
|
||||
ViewDisplayTest* view_display_test_alloc();
|
||||
|
||||
void view_display_test_free(ViewDisplayTest* instance);
|
||||
|
||||
View* view_display_test_get_view(ViewDisplayTest* instance);
|
11
applications/debug/file_browser_test/application.fam
Normal file
11
applications/debug/file_browser_test/application.fam
Normal file
@@ -0,0 +1,11 @@
|
||||
App(
|
||||
appid="file_browser_test",
|
||||
name="File Browser Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="file_browser_app",
|
||||
cdefines=["APP_FILE_BROWSER_TEST"],
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=150,
|
||||
fap_category="Debug",
|
||||
)
|
99
applications/debug/file_browser_test/file_browser_app.c
Normal file
99
applications/debug/file_browser_test/file_browser_app.c
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "assets_icons.h"
|
||||
#include "file_browser_app_i.h"
|
||||
#include "gui/modules/file_browser.h"
|
||||
#include "m-string.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <storage/storage.h>
|
||||
#include <lib/toolbox/path.h>
|
||||
|
||||
static bool file_browser_app_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
FileBrowserApp* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool file_browser_app_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
FileBrowserApp* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void file_browser_app_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
FileBrowserApp* app = context;
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
FileBrowserApp* file_browser_app_alloc(char* arg) {
|
||||
UNUSED(arg);
|
||||
FileBrowserApp* app = malloc(sizeof(FileBrowserApp));
|
||||
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
|
||||
app->scene_manager = scene_manager_alloc(&file_browser_scene_handlers, app);
|
||||
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
app->view_dispatcher, file_browser_app_tick_event_callback, 500);
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
app->view_dispatcher, file_browser_app_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, file_browser_app_back_event_callback);
|
||||
|
||||
app->widget = widget_alloc();
|
||||
|
||||
string_init(app->file_path);
|
||||
app->file_browser = file_browser_alloc(app->file_path);
|
||||
file_browser_configure(app->file_browser, "*", true, &I_badusb_10px, true);
|
||||
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, FileBrowserAppViewStart, widget_get_view(app->widget));
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, FileBrowserAppViewResult, widget_get_view(app->widget));
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, FileBrowserAppViewBrowser, file_browser_get_view(app->file_browser));
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, FileBrowserSceneStart);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void file_browser_app_free(FileBrowserApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// Views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, FileBrowserAppViewStart);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, FileBrowserAppViewResult);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, FileBrowserAppViewBrowser);
|
||||
widget_free(app->widget);
|
||||
file_browser_free(app->file_browser);
|
||||
|
||||
// View dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
// Close records
|
||||
furi_record_close(RECORD_GUI);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
furi_record_close(RECORD_DIALOGS);
|
||||
|
||||
string_clear(app->file_path);
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t file_browser_app(void* p) {
|
||||
FileBrowserApp* file_browser_app = file_browser_app_alloc((char*)p);
|
||||
|
||||
view_dispatcher_run(file_browser_app->view_dispatcher);
|
||||
|
||||
file_browser_app_free(file_browser_app);
|
||||
return 0;
|
||||
}
|
32
applications/debug/file_browser_test/file_browser_app_i.h
Normal file
32
applications/debug/file_browser_test/file_browser_app_i.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "scenes/file_browser_scene.h"
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/file_browser.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/modules/widget.h>
|
||||
|
||||
typedef struct FileBrowserApp FileBrowserApp;
|
||||
|
||||
struct FileBrowserApp {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
DialogsApp* dialogs;
|
||||
Widget* widget;
|
||||
FileBrowser* file_browser;
|
||||
|
||||
string_t file_path;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
FileBrowserAppViewStart,
|
||||
FileBrowserAppViewBrowser,
|
||||
FileBrowserAppViewResult,
|
||||
} FileBrowserAppView;
|
@@ -0,0 +1,30 @@
|
||||
#include "file_browser_scene.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const file_browser_scene_on_enter_handlers[])(void*) = {
|
||||
#include "file_browser_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||
bool (*const file_browser_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "file_browser_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||
void (*const file_browser_scene_on_exit_handlers[])(void* context) = {
|
||||
#include "file_browser_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers file_browser_scene_handlers = {
|
||||
.on_enter_handlers = file_browser_scene_on_enter_handlers,
|
||||
.on_event_handlers = file_browser_scene_on_event_handlers,
|
||||
.on_exit_handlers = file_browser_scene_on_exit_handlers,
|
||||
.scene_num = FileBrowserSceneNum,
|
||||
};
|
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) FileBrowserScene##id,
|
||||
typedef enum {
|
||||
#include "file_browser_scene_config.h"
|
||||
FileBrowserSceneNum,
|
||||
} FileBrowserScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers file_browser_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "file_browser_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) \
|
||||
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
||||
#include "file_browser_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
||||
#include "file_browser_scene_config.h"
|
||||
#undef ADD_SCENE
|
@@ -0,0 +1,43 @@
|
||||
#include "../file_browser_app_i.h"
|
||||
#include <core/check.h>
|
||||
#include <core/log.h>
|
||||
#include "furi_hal.h"
|
||||
#include "m-string.h"
|
||||
|
||||
#define DEFAULT_PATH "/"
|
||||
#define EXTENSION "*"
|
||||
|
||||
bool file_browser_scene_browser_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
FileBrowserApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
scene_manager_next_scene(app->scene_manager, FileBrowserSceneResult);
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
static void file_browser_callback(void* context) {
|
||||
FileBrowserApp* app = context;
|
||||
furi_assert(app);
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, SceneManagerEventTypeCustom);
|
||||
}
|
||||
|
||||
void file_browser_scene_browser_on_enter(void* context) {
|
||||
FileBrowserApp* app = context;
|
||||
|
||||
file_browser_set_callback(app->file_browser, file_browser_callback, app);
|
||||
|
||||
file_browser_start(app->file_browser, app->file_path);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, FileBrowserAppViewBrowser);
|
||||
}
|
||||
|
||||
void file_browser_scene_browser_on_exit(void* context) {
|
||||
FileBrowserApp* app = context;
|
||||
|
||||
file_browser_stop(app->file_browser);
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
ADD_SCENE(file_browser, start, Start)
|
||||
ADD_SCENE(file_browser, browser, Browser)
|
||||
ADD_SCENE(file_browser, result, Result)
|
@@ -0,0 +1,36 @@
|
||||
#include "../file_browser_app_i.h"
|
||||
#include "furi_hal.h"
|
||||
#include "m-string.h"
|
||||
|
||||
void file_browser_scene_result_ok_callback(InputType type, void* context) {
|
||||
furi_assert(context);
|
||||
FileBrowserApp* app = context;
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, type);
|
||||
}
|
||||
|
||||
bool file_browser_scene_result_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
//FileBrowserApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void file_browser_scene_result_on_enter(void* context) {
|
||||
FileBrowserApp* app = context;
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
app->widget, 64, 10, AlignCenter, AlignTop, FontSecondary, string_get_cstr(app->file_path));
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, FileBrowserAppViewResult);
|
||||
}
|
||||
|
||||
void file_browser_scene_result_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
FileBrowserApp* app = context;
|
||||
widget_reset(app->widget);
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
#include "../file_browser_app_i.h"
|
||||
|
||||
#include <furi_hal.h>
|
||||
#include <gui/modules/widget_elements/widget_element_i.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
static void
|
||||
file_browser_scene_start_ok_callback(GuiButtonType result, InputType type, void* context) {
|
||||
UNUSED(result);
|
||||
furi_assert(context);
|
||||
FileBrowserApp* app = context;
|
||||
if(type == InputTypeShort) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, type);
|
||||
}
|
||||
}
|
||||
|
||||
bool file_browser_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
FileBrowserApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
string_set_str(app->file_path, ANY_PATH("badusb/demo_windows.txt"));
|
||||
scene_manager_next_scene(app->scene_manager, FileBrowserSceneBrowser);
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void file_browser_scene_start_on_enter(void* context) {
|
||||
FileBrowserApp* app = context;
|
||||
|
||||
widget_add_string_multiline_element(
|
||||
app->widget, 64, 20, AlignCenter, AlignTop, FontSecondary, "Press OK to start");
|
||||
|
||||
widget_add_button_element(
|
||||
app->widget, GuiButtonTypeCenter, "Ok", file_browser_scene_start_ok_callback, app);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, FileBrowserAppViewStart);
|
||||
}
|
||||
|
||||
void file_browser_scene_start_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
FileBrowserApp* app = context;
|
||||
widget_reset(app->widget);
|
||||
}
|
11
applications/debug/keypad_test/application.fam
Normal file
11
applications/debug/keypad_test/application.fam
Normal file
@@ -0,0 +1,11 @@
|
||||
App(
|
||||
appid="keypad_test",
|
||||
name="Keypad Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="keypad_test_app",
|
||||
cdefines=["APP_KEYPAD_TEST"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=30,
|
||||
fap_category="Debug",
|
||||
)
|
155
applications/debug/keypad_test/keypad_test.c
Normal file
155
applications/debug/keypad_test/keypad_test.c
Normal file
@@ -0,0 +1,155 @@
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
|
||||
#define TAG "KeypadTest"
|
||||
|
||||
typedef struct {
|
||||
bool press[5];
|
||||
uint16_t up;
|
||||
uint16_t down;
|
||||
uint16_t left;
|
||||
uint16_t right;
|
||||
uint16_t ok;
|
||||
} KeypadTestState;
|
||||
|
||||
static void keypad_test_reset_state(KeypadTestState* state) {
|
||||
state->left = 0;
|
||||
state->right = 0;
|
||||
state->up = 0;
|
||||
state->down = 0;
|
||||
state->ok = 0;
|
||||
}
|
||||
|
||||
static void keypad_test_render_callback(Canvas* canvas, void* ctx) {
|
||||
KeypadTestState* state = (KeypadTestState*)acquire_mutex((ValueMutex*)ctx, 25);
|
||||
canvas_clear(canvas);
|
||||
char strings[5][20];
|
||||
|
||||
snprintf(strings[0], 20, "Ok: %d", state->ok);
|
||||
snprintf(strings[1], 20, "L: %d", state->left);
|
||||
snprintf(strings[2], 20, "R: %d", state->right);
|
||||
snprintf(strings[3], 20, "U: %d", state->up);
|
||||
snprintf(strings[4], 20, "D: %d", state->down);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 0, 10, "Keypad test");
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 0, 24, strings[1]);
|
||||
canvas_draw_str(canvas, 35, 24, strings[2]);
|
||||
canvas_draw_str(canvas, 0, 36, strings[3]);
|
||||
canvas_draw_str(canvas, 35, 36, strings[4]);
|
||||
canvas_draw_str(canvas, 0, 48, strings[0]);
|
||||
canvas_draw_circle(canvas, 100, 26, 25);
|
||||
|
||||
if(state->press[0]) canvas_draw_disc(canvas, 118, 26, 5);
|
||||
if(state->press[1]) canvas_draw_disc(canvas, 82, 26, 5);
|
||||
if(state->press[2]) canvas_draw_disc(canvas, 100, 8, 5);
|
||||
if(state->press[3]) canvas_draw_disc(canvas, 100, 44, 5);
|
||||
if(state->press[4]) canvas_draw_disc(canvas, 100, 26, 5);
|
||||
|
||||
canvas_draw_str(canvas, 10, 63, "[back] - reset, hold to exit");
|
||||
|
||||
release_mutex((ValueMutex*)ctx, state);
|
||||
}
|
||||
|
||||
static void keypad_test_input_callback(InputEvent* input_event, void* ctx) {
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
|
||||
}
|
||||
|
||||
int32_t keypad_test_app(void* p) {
|
||||
UNUSED(p);
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent));
|
||||
furi_check(event_queue);
|
||||
|
||||
KeypadTestState _state = {{false, false, false, false, false}, 0, 0, 0, 0, 0};
|
||||
|
||||
ValueMutex state_mutex;
|
||||
if(!init_mutex(&state_mutex, &_state, sizeof(KeypadTestState))) {
|
||||
FURI_LOG_E(TAG, "cannot create mutex");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
|
||||
view_port_draw_callback_set(view_port, keypad_test_render_callback, &state_mutex);
|
||||
view_port_input_callback_set(view_port, keypad_test_input_callback, event_queue);
|
||||
|
||||
// Open GUI and register view_port
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
InputEvent event;
|
||||
while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
|
||||
KeypadTestState* state = (KeypadTestState*)acquire_mutex_block(&state_mutex);
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"key: %s type: %s",
|
||||
input_get_key_name(event.key),
|
||||
input_get_type_name(event.type));
|
||||
|
||||
if(event.key == InputKeyRight) {
|
||||
if(event.type == InputTypePress) {
|
||||
state->press[0] = true;
|
||||
} else if(event.type == InputTypeRelease) {
|
||||
state->press[0] = false;
|
||||
} else if(event.type == InputTypeShort) {
|
||||
++state->right;
|
||||
}
|
||||
} else if(event.key == InputKeyLeft) {
|
||||
if(event.type == InputTypePress) {
|
||||
state->press[1] = true;
|
||||
} else if(event.type == InputTypeRelease) {
|
||||
state->press[1] = false;
|
||||
} else if(event.type == InputTypeShort) {
|
||||
++state->left;
|
||||
}
|
||||
} else if(event.key == InputKeyUp) {
|
||||
if(event.type == InputTypePress) {
|
||||
state->press[2] = true;
|
||||
} else if(event.type == InputTypeRelease) {
|
||||
state->press[2] = false;
|
||||
} else if(event.type == InputTypeShort) {
|
||||
++state->up;
|
||||
}
|
||||
} else if(event.key == InputKeyDown) {
|
||||
if(event.type == InputTypePress) {
|
||||
state->press[3] = true;
|
||||
} else if(event.type == InputTypeRelease) {
|
||||
state->press[3] = false;
|
||||
} else if(event.type == InputTypeShort) {
|
||||
++state->down;
|
||||
}
|
||||
} else if(event.key == InputKeyOk) {
|
||||
if(event.type == InputTypePress) {
|
||||
state->press[4] = true;
|
||||
} else if(event.type == InputTypeRelease) {
|
||||
state->press[4] = false;
|
||||
} else if(event.type == InputTypeShort) {
|
||||
++state->ok;
|
||||
}
|
||||
} else if(event.key == InputKeyBack) {
|
||||
if(event.type == InputTypeLong) {
|
||||
release_mutex(&state_mutex, state);
|
||||
break;
|
||||
} else if(event.type == InputTypeShort) {
|
||||
keypad_test_reset_state(state);
|
||||
}
|
||||
}
|
||||
|
||||
release_mutex(&state_mutex, state);
|
||||
view_port_update(view_port);
|
||||
}
|
||||
|
||||
// remove & free all stuff created by app
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
delete_mutex(&state_mutex);
|
||||
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
return 0;
|
||||
}
|
15
applications/debug/lfrfid_debug/application.fam
Normal file
15
applications/debug/lfrfid_debug/application.fam
Normal file
@@ -0,0 +1,15 @@
|
||||
App(
|
||||
appid="lfrfid_debug",
|
||||
name="LF-RFID Debug",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="lfrfid_debug_app",
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
provides=[
|
||||
"lfrfid_debug",
|
||||
],
|
||||
stack_size=1 * 1024,
|
||||
order=100,
|
||||
fap_category="Debug",
|
||||
)
|
81
applications/debug/lfrfid_debug/lfrfid_debug.c
Normal file
81
applications/debug/lfrfid_debug/lfrfid_debug.c
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "lfrfid_debug_i.h"
|
||||
|
||||
static bool lfrfid_debug_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
LfRfidDebug* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool lfrfid_debug_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
LfRfidDebug* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static LfRfidDebug* lfrfid_debug_alloc() {
|
||||
LfRfidDebug* app = malloc(sizeof(LfRfidDebug));
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
app->scene_manager = scene_manager_alloc(&lfrfid_debug_scene_handlers, app);
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
app->view_dispatcher, lfrfid_debug_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, lfrfid_debug_back_event_callback);
|
||||
|
||||
// Open GUI record
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// Submenu
|
||||
app->submenu = submenu_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, LfRfidDebugViewSubmenu, submenu_get_view(app->submenu));
|
||||
|
||||
// Tune view
|
||||
app->tune_view = lfrfid_debug_view_tune_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
LfRfidDebugViewTune,
|
||||
lfrfid_debug_view_tune_get_view(app->tune_view));
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
static void lfrfid_debug_free(LfRfidDebug* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// Submenu
|
||||
view_dispatcher_remove_view(app->view_dispatcher, LfRfidDebugViewSubmenu);
|
||||
submenu_free(app->submenu);
|
||||
|
||||
// Tune view
|
||||
view_dispatcher_remove_view(app->view_dispatcher, LfRfidDebugViewTune);
|
||||
lfrfid_debug_view_tune_free(app->tune_view);
|
||||
|
||||
// View Dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
|
||||
// Scene Manager
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
// GUI
|
||||
furi_record_close(RECORD_GUI);
|
||||
app->gui = NULL;
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t lfrfid_debug_app(void* p) {
|
||||
UNUSED(p);
|
||||
LfRfidDebug* app = lfrfid_debug_alloc();
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, LfRfidDebugSceneStart);
|
||||
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
|
||||
lfrfid_debug_free(app);
|
||||
|
||||
return 0;
|
||||
}
|
30
applications/debug/lfrfid_debug/lfrfid_debug_i.h
Normal file
30
applications/debug/lfrfid_debug/lfrfid_debug_i.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
#include <gui/modules/submenu.h>
|
||||
|
||||
#include "views/lfrfid_debug_view_tune.h"
|
||||
#include "scenes/lfrfid_debug_scene.h"
|
||||
|
||||
typedef struct LfRfidDebug LfRfidDebug;
|
||||
|
||||
struct LfRfidDebug {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
|
||||
// Common Views
|
||||
Submenu* submenu;
|
||||
LfRfidTuneView* tune_view;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LfRfidDebugViewSubmenu,
|
||||
LfRfidDebugViewTune,
|
||||
} LfRfidDebugView;
|
@@ -0,0 +1,44 @@
|
||||
#include "../lfrfid_debug_i.h"
|
||||
|
||||
typedef enum {
|
||||
SubmenuIndexTune,
|
||||
} SubmenuIndex;
|
||||
|
||||
static void lfrfid_debug_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||
LfRfidDebug* app = context;
|
||||
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void lfrfid_debug_scene_start_on_enter(void* context) {
|
||||
LfRfidDebug* app = context;
|
||||
Submenu* submenu = app->submenu;
|
||||
|
||||
submenu_add_item(
|
||||
submenu, "Tune", SubmenuIndexTune, lfrfid_debug_scene_start_submenu_callback, app);
|
||||
|
||||
submenu_set_selected_item(
|
||||
submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidDebugSceneStart));
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidDebugViewSubmenu);
|
||||
}
|
||||
|
||||
bool lfrfid_debug_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
LfRfidDebug* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if(event.event == SubmenuIndexTune) {
|
||||
scene_manager_next_scene(app->scene_manager, LfRfidDebugSceneTune);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void lfrfid_debug_scene_start_on_exit(void* context) {
|
||||
LfRfidDebug* app = context;
|
||||
|
||||
submenu_reset(app->submenu);
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
#include "../lfrfid_debug_i.h"
|
||||
#include <furi_hal.h>
|
||||
|
||||
static void comparator_trigger_callback(bool level, void* comp_ctx) {
|
||||
UNUSED(comp_ctx);
|
||||
furi_hal_gpio_write(&gpio_ext_pa7, !level);
|
||||
}
|
||||
|
||||
void lfrfid_debug_scene_tune_on_enter(void* context) {
|
||||
LfRfidDebug* app = context;
|
||||
|
||||
furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull);
|
||||
|
||||
furi_hal_rfid_comp_set_callback(comparator_trigger_callback, app);
|
||||
furi_hal_rfid_comp_start();
|
||||
|
||||
furi_hal_rfid_pins_read();
|
||||
furi_hal_rfid_tim_read(125000, 0.5);
|
||||
furi_hal_rfid_tim_read_start();
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidDebugViewTune);
|
||||
}
|
||||
|
||||
bool lfrfid_debug_scene_tune_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(event);
|
||||
|
||||
LfRfidDebug* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(lfrfid_debug_view_tune_is_dirty(app->tune_view)) {
|
||||
furi_hal_rfid_set_read_period(lfrfid_debug_view_tune_get_arr(app->tune_view));
|
||||
furi_hal_rfid_set_read_pulse(lfrfid_debug_view_tune_get_ccr(app->tune_view));
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void lfrfid_debug_scene_tune_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
|
||||
furi_hal_rfid_comp_stop();
|
||||
furi_hal_rfid_comp_set_callback(NULL, NULL);
|
||||
|
||||
furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog);
|
||||
furi_hal_rfid_tim_read_stop();
|
||||
furi_hal_rfid_tim_reset();
|
||||
furi_hal_rfid_pins_reset();
|
||||
}
|
30
applications/debug/lfrfid_debug/scenes/lfrfid_debug_scene.c
Normal file
30
applications/debug/lfrfid_debug/scenes/lfrfid_debug_scene.c
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "lfrfid_debug_scene.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const lfrfid_debug_on_enter_handlers[])(void*) = {
|
||||
#include "lfrfid_debug_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||
bool (*const lfrfid_debug_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "lfrfid_debug_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||
void (*const lfrfid_debug_on_exit_handlers[])(void* context) = {
|
||||
#include "lfrfid_debug_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers lfrfid_debug_scene_handlers = {
|
||||
.on_enter_handlers = lfrfid_debug_on_enter_handlers,
|
||||
.on_event_handlers = lfrfid_debug_on_event_handlers,
|
||||
.on_exit_handlers = lfrfid_debug_on_exit_handlers,
|
||||
.scene_num = LfRfidDebugSceneNum,
|
||||
};
|
29
applications/debug/lfrfid_debug/scenes/lfrfid_debug_scene.h
Normal file
29
applications/debug/lfrfid_debug/scenes/lfrfid_debug_scene.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) LfRfidDebugScene##id,
|
||||
typedef enum {
|
||||
#include "lfrfid_debug_scene_config.h"
|
||||
LfRfidDebugSceneNum,
|
||||
} LfRfidDebugScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers lfrfid_debug_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "lfrfid_debug_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) \
|
||||
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
|
||||
#include "lfrfid_debug_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
|
||||
#include "lfrfid_debug_scene_config.h"
|
||||
#undef ADD_SCENE
|
@@ -0,0 +1,2 @@
|
||||
ADD_SCENE(lfrfid_debug, start, Start)
|
||||
ADD_SCENE(lfrfid_debug, tune, Tune)
|
229
applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c
Normal file
229
applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c
Normal file
@@ -0,0 +1,229 @@
|
||||
#include "lfrfid_debug_view_tune.h"
|
||||
#include <gui/elements.h>
|
||||
|
||||
#define TEMP_STR_LEN 128
|
||||
|
||||
struct LfRfidTuneView {
|
||||
View* view;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
bool dirty;
|
||||
bool fine;
|
||||
uint32_t ARR;
|
||||
uint32_t CCR;
|
||||
int pos;
|
||||
} LfRfidTuneViewModel;
|
||||
|
||||
static void lfrfid_debug_view_tune_draw_callback(Canvas* canvas, void* _model) {
|
||||
LfRfidTuneViewModel* model = _model;
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
if(model->fine) {
|
||||
canvas_draw_box(
|
||||
canvas,
|
||||
128 - canvas_string_width(canvas, "Fine") - 4,
|
||||
0,
|
||||
canvas_string_width(canvas, "Fine") + 4,
|
||||
canvas_current_font_height(canvas) + 1);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_str_aligned(canvas, 128 - 2, 2, AlignRight, AlignTop, "Fine");
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
char buffer[TEMP_STR_LEN + 1];
|
||||
double freq = ((float)SystemCoreClock / ((float)model->ARR + 1));
|
||||
double duty = ((float)model->CCR + 1) / ((float)model->ARR + 1) * 100.0f;
|
||||
snprintf(
|
||||
buffer,
|
||||
TEMP_STR_LEN,
|
||||
"%sARR: %lu\n"
|
||||
"freq = %.4f\n"
|
||||
"%sCCR: %lu\n"
|
||||
"duty = %.4f",
|
||||
model->pos == 0 ? ">" : "",
|
||||
model->ARR,
|
||||
freq,
|
||||
model->pos == 1 ? ">" : "",
|
||||
model->CCR,
|
||||
duty);
|
||||
elements_multiline_text_aligned(canvas, 2, 2, AlignLeft, AlignTop, buffer);
|
||||
}
|
||||
|
||||
static void lfrfid_debug_view_tune_button_up(LfRfidTuneView* tune_view) {
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
if(model->pos > 0) model->pos--;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void lfrfid_debug_view_tune_button_down(LfRfidTuneView* tune_view) {
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
if(model->pos < 1) model->pos++;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void lfrfid_debug_view_tune_button_left(LfRfidTuneView* tune_view) {
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
if(model->pos == 0) {
|
||||
if(model->fine) {
|
||||
model->ARR -= 1;
|
||||
} else {
|
||||
model->ARR -= 10;
|
||||
}
|
||||
} else if(model->pos == 1) {
|
||||
if(model->fine) {
|
||||
model->CCR -= 1;
|
||||
} else {
|
||||
model->CCR -= 10;
|
||||
}
|
||||
}
|
||||
|
||||
model->dirty = true;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void lfrfid_debug_view_tune_button_right(LfRfidTuneView* tune_view) {
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
if(model->pos == 0) {
|
||||
if(model->fine) {
|
||||
model->ARR += 1;
|
||||
} else {
|
||||
model->ARR += 10;
|
||||
}
|
||||
} else if(model->pos == 1) {
|
||||
if(model->fine) {
|
||||
model->CCR += 1;
|
||||
} else {
|
||||
model->CCR += 10;
|
||||
}
|
||||
}
|
||||
|
||||
model->dirty = true;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static void lfrfid_debug_view_tune_button_ok(LfRfidTuneView* tune_view) {
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
model->fine = !model->fine;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
static bool lfrfid_debug_view_tune_input_callback(InputEvent* event, void* context) {
|
||||
LfRfidTuneView* tune_view = context;
|
||||
bool consumed = false;
|
||||
|
||||
// Process key presses only
|
||||
if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
|
||||
consumed = true;
|
||||
|
||||
switch(event->key) {
|
||||
case InputKeyLeft:
|
||||
lfrfid_debug_view_tune_button_left(tune_view);
|
||||
break;
|
||||
case InputKeyRight:
|
||||
lfrfid_debug_view_tune_button_right(tune_view);
|
||||
break;
|
||||
case InputKeyUp:
|
||||
lfrfid_debug_view_tune_button_up(tune_view);
|
||||
break;
|
||||
case InputKeyDown:
|
||||
lfrfid_debug_view_tune_button_down(tune_view);
|
||||
break;
|
||||
case InputKeyOk:
|
||||
lfrfid_debug_view_tune_button_ok(tune_view);
|
||||
break;
|
||||
default:
|
||||
consumed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
LfRfidTuneView* lfrfid_debug_view_tune_alloc() {
|
||||
LfRfidTuneView* tune_view = malloc(sizeof(LfRfidTuneView));
|
||||
tune_view->view = view_alloc();
|
||||
view_set_context(tune_view->view, tune_view);
|
||||
view_allocate_model(tune_view->view, ViewModelTypeLocking, sizeof(LfRfidTuneViewModel));
|
||||
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
model->dirty = true;
|
||||
model->fine = false;
|
||||
model->ARR = 511;
|
||||
model->CCR = 255;
|
||||
model->pos = 0;
|
||||
return true;
|
||||
});
|
||||
|
||||
view_set_draw_callback(tune_view->view, lfrfid_debug_view_tune_draw_callback);
|
||||
view_set_input_callback(tune_view->view, lfrfid_debug_view_tune_input_callback);
|
||||
|
||||
return tune_view;
|
||||
}
|
||||
|
||||
void lfrfid_debug_view_tune_free(LfRfidTuneView* tune_view) {
|
||||
view_free(tune_view->view);
|
||||
free(tune_view);
|
||||
}
|
||||
|
||||
View* lfrfid_debug_view_tune_get_view(LfRfidTuneView* tune_view) {
|
||||
return tune_view->view;
|
||||
}
|
||||
|
||||
void lfrfid_debug_view_tune_clean(LfRfidTuneView* tune_view) {
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
model->dirty = true;
|
||||
model->fine = false;
|
||||
model->ARR = 511;
|
||||
model->CCR = 255;
|
||||
model->pos = 0;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
bool lfrfid_debug_view_tune_is_dirty(LfRfidTuneView* tune_view) {
|
||||
bool result = false;
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
result = model->dirty;
|
||||
model->dirty = false;
|
||||
return false;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t lfrfid_debug_view_tune_get_arr(LfRfidTuneView* tune_view) {
|
||||
uint32_t result = false;
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
result = model->ARR;
|
||||
return false;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view) {
|
||||
uint32_t result = false;
|
||||
with_view_model(
|
||||
tune_view->view, (LfRfidTuneViewModel * model) {
|
||||
result = model->CCR;
|
||||
return false;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include <gui/view.h>
|
||||
|
||||
typedef struct LfRfidTuneView LfRfidTuneView;
|
||||
|
||||
LfRfidTuneView* lfrfid_debug_view_tune_alloc();
|
||||
|
||||
void lfrfid_debug_view_tune_free(LfRfidTuneView* tune_view);
|
||||
|
||||
View* lfrfid_debug_view_tune_get_view(LfRfidTuneView* tune_view);
|
||||
|
||||
void lfrfid_debug_view_tune_clean(LfRfidTuneView* tune_view);
|
||||
|
||||
bool lfrfid_debug_view_tune_is_dirty(LfRfidTuneView* tune_view);
|
||||
|
||||
uint32_t lfrfid_debug_view_tune_get_arr(LfRfidTuneView* tune_view);
|
||||
|
||||
uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view);
|
11
applications/debug/text_box_test/application.fam
Normal file
11
applications/debug/text_box_test/application.fam
Normal file
@@ -0,0 +1,11 @@
|
||||
App(
|
||||
appid="text_box_test",
|
||||
name="Text Box Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="text_box_test_app",
|
||||
cdefines=["APP_TEXT_BOX_TEST"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=140,
|
||||
fap_category="Debug",
|
||||
)
|
127
applications/debug/text_box_test/text_box_test.c
Normal file
127
applications/debug/text_box_test/text_box_test.c
Normal file
@@ -0,0 +1,127 @@
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
#define TAG "TextBoxTest"
|
||||
|
||||
static void text_box_center_top_secondary_128x22(Canvas* canvas) {
|
||||
canvas_draw_frame(canvas, 0, 0, 128, 22);
|
||||
elements_text_box(canvas, 0, 0, 128, 22, AlignCenter, AlignTop, "secondary font test", false);
|
||||
}
|
||||
|
||||
static void text_box_right_bottom_bold_128x22(Canvas* canvas) {
|
||||
canvas_draw_frame(canvas, 0, 0, 128, 22);
|
||||
elements_text_box(
|
||||
canvas, 0, 0, 128, 22, AlignRight, AlignBottom, "\e#Bold font test\e#", false);
|
||||
}
|
||||
|
||||
static void text_box_left_center_mixed_80x50(Canvas* canvas) {
|
||||
canvas_draw_frame(canvas, 0, 0, 80, 50);
|
||||
elements_text_box(
|
||||
canvas,
|
||||
0,
|
||||
0,
|
||||
80,
|
||||
50,
|
||||
AlignLeft,
|
||||
AlignCenter,
|
||||
"\e#Never\e# gonna give you up\n\e!Never\e! gonna let you down",
|
||||
false);
|
||||
}
|
||||
|
||||
static void text_box_center_center_secondary_110x44(Canvas* canvas) {
|
||||
canvas_draw_frame(canvas, 4, 20, 110, 30);
|
||||
elements_text_box(
|
||||
canvas,
|
||||
4,
|
||||
20,
|
||||
110,
|
||||
30,
|
||||
AlignCenter,
|
||||
AlignCenter,
|
||||
"Loooooooooooooo0000000ooong file name from happy 100500 Flipper 0wners",
|
||||
true);
|
||||
}
|
||||
|
||||
static void (*text_box_test_render[])(Canvas* canvas) = {
|
||||
text_box_center_top_secondary_128x22,
|
||||
text_box_right_bottom_bold_128x22,
|
||||
text_box_left_center_mixed_80x50,
|
||||
text_box_center_center_secondary_110x44,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t idx;
|
||||
} TextBoxTestState;
|
||||
|
||||
static void text_box_test_render_callback(Canvas* canvas, void* ctx) {
|
||||
TextBoxTestState* state = acquire_mutex((ValueMutex*)ctx, 25);
|
||||
canvas_clear(canvas);
|
||||
|
||||
text_box_test_render[state->idx](canvas);
|
||||
|
||||
release_mutex((ValueMutex*)ctx, state);
|
||||
}
|
||||
|
||||
static void text_box_test_input_callback(InputEvent* input_event, void* ctx) {
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
|
||||
}
|
||||
|
||||
int32_t text_box_test_app(void* p) {
|
||||
UNUSED(p);
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(32, sizeof(InputEvent));
|
||||
furi_check(event_queue);
|
||||
|
||||
TextBoxTestState _state = {.idx = 0};
|
||||
|
||||
ValueMutex state_mutex;
|
||||
if(!init_mutex(&state_mutex, &_state, sizeof(TextBoxTestState))) {
|
||||
FURI_LOG_E(TAG, "Cannot create mutex");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
|
||||
view_port_draw_callback_set(view_port, text_box_test_render_callback, &state_mutex);
|
||||
view_port_input_callback_set(view_port, text_box_test_input_callback, event_queue);
|
||||
|
||||
// Open GUI and register view_port
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
uint32_t test_renders_num = COUNT_OF(text_box_test_render);
|
||||
InputEvent event;
|
||||
while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
|
||||
TextBoxTestState* state = acquire_mutex_block(&state_mutex);
|
||||
|
||||
if(event.type == InputTypeShort) {
|
||||
if(event.key == InputKeyRight) {
|
||||
if(state->idx < test_renders_num - 1) {
|
||||
state->idx++;
|
||||
}
|
||||
} else if(event.key == InputKeyLeft) {
|
||||
if(state->idx > 0) {
|
||||
state->idx--;
|
||||
}
|
||||
} else if(event.key == InputKeyBack) {
|
||||
release_mutex(&state_mutex, state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
release_mutex(&state_mutex, state);
|
||||
view_port_update(view_port);
|
||||
}
|
||||
|
||||
// remove & free all stuff created by app
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
delete_mutex(&state_mutex);
|
||||
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
return 0;
|
||||
}
|
11
applications/debug/uart_echo/application.fam
Normal file
11
applications/debug/uart_echo/application.fam
Normal file
@@ -0,0 +1,11 @@
|
||||
App(
|
||||
appid="uart_echo",
|
||||
name="UART Echo",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="uart_echo_app",
|
||||
cdefines=["APP_UART_ECHO"],
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=70,
|
||||
fap_category="Debug",
|
||||
)
|
275
applications/debug/uart_echo/uart_echo.c
Normal file
275
applications/debug/uart_echo/uart_echo.c
Normal file
@@ -0,0 +1,275 @@
|
||||
#include <furi.h>
|
||||
#include <m-string.h>
|
||||
#include <gui/gui.h>
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <gui/elements.h>
|
||||
#include <stream_buffer.h>
|
||||
#include <furi_hal_uart.h>
|
||||
#include <furi_hal_console.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
|
||||
#define LINES_ON_SCREEN 6
|
||||
#define COLUMNS_ON_SCREEN 21
|
||||
|
||||
typedef struct UartDumpModel UartDumpModel;
|
||||
|
||||
typedef struct {
|
||||
Gui* gui;
|
||||
NotificationApp* notification;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
View* view;
|
||||
FuriThread* worker_thread;
|
||||
StreamBufferHandle_t rx_stream;
|
||||
} UartEchoApp;
|
||||
|
||||
typedef struct {
|
||||
string_t text;
|
||||
} ListElement;
|
||||
|
||||
struct UartDumpModel {
|
||||
ListElement* list[LINES_ON_SCREEN];
|
||||
uint8_t line;
|
||||
|
||||
char last_char;
|
||||
bool escape;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event
|
||||
WorkerEventStop = (1 << 1),
|
||||
WorkerEventRx = (1 << 2),
|
||||
} WorkerEventFlags;
|
||||
|
||||
#define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx)
|
||||
|
||||
const NotificationSequence sequence_notification = {
|
||||
&message_display_backlight_on,
|
||||
&message_green_255,
|
||||
&message_delay_10,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void uart_echo_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
UartDumpModel* model = _model;
|
||||
|
||||
// Prepare canvas
|
||||
canvas_clear(canvas);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontKeyboard);
|
||||
|
||||
for(size_t i = 0; i < LINES_ON_SCREEN; i++) {
|
||||
canvas_draw_str(
|
||||
canvas,
|
||||
0,
|
||||
(i + 1) * (canvas_current_font_height(canvas) - 1),
|
||||
string_get_cstr(model->list[i]->text));
|
||||
|
||||
if(i == model->line) {
|
||||
uint8_t width = canvas_string_width(canvas, string_get_cstr(model->list[i]->text));
|
||||
|
||||
canvas_draw_box(
|
||||
canvas,
|
||||
width,
|
||||
(i) * (canvas_current_font_height(canvas) - 1) + 2,
|
||||
2,
|
||||
canvas_current_font_height(canvas) - 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool uart_echo_view_input_callback(InputEvent* event, void* context) {
|
||||
UNUSED(event);
|
||||
UNUSED(context);
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint32_t uart_echo_exit(void* context) {
|
||||
UNUSED(context);
|
||||
return VIEW_NONE;
|
||||
}
|
||||
|
||||
static void uart_echo_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
|
||||
furi_assert(context);
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
UartEchoApp* app = context;
|
||||
|
||||
if(ev == UartIrqEventRXNE) {
|
||||
xStreamBufferSendFromISR(app->rx_stream, &data, 1, &xHigherPriorityTaskWoken);
|
||||
furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventRx);
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
}
|
||||
|
||||
static void uart_echo_push_to_list(UartDumpModel* model, const char data) {
|
||||
if(model->escape) {
|
||||
// escape code end with letter
|
||||
if((data >= 'a' && data <= 'z') || (data >= 'A' && data <= 'Z')) {
|
||||
model->escape = false;
|
||||
}
|
||||
} else if(data == '[' && model->last_char == '\e') {
|
||||
// "Esc[" is a escape code
|
||||
model->escape = true;
|
||||
} else if((data >= ' ' && data <= '~') || (data == '\n' || data == '\r')) {
|
||||
bool new_string_needed = false;
|
||||
if(string_size(model->list[model->line]->text) >= COLUMNS_ON_SCREEN) {
|
||||
new_string_needed = true;
|
||||
} else if((data == '\n' || data == '\r')) {
|
||||
// pack line breaks
|
||||
if(model->last_char != '\n' && model->last_char != '\r') {
|
||||
new_string_needed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(new_string_needed) {
|
||||
if((model->line + 1) < LINES_ON_SCREEN) {
|
||||
model->line += 1;
|
||||
} else {
|
||||
ListElement* first = model->list[0];
|
||||
|
||||
for(size_t i = 1; i < LINES_ON_SCREEN; i++) {
|
||||
model->list[i - 1] = model->list[i];
|
||||
}
|
||||
|
||||
string_reset(first->text);
|
||||
model->list[model->line] = first;
|
||||
}
|
||||
}
|
||||
|
||||
if(data != '\n' && data != '\r') {
|
||||
string_push_back(model->list[model->line]->text, data);
|
||||
}
|
||||
}
|
||||
model->last_char = data;
|
||||
}
|
||||
|
||||
static int32_t uart_echo_worker(void* context) {
|
||||
furi_assert(context);
|
||||
UartEchoApp* app = context;
|
||||
|
||||
while(1) {
|
||||
uint32_t events =
|
||||
furi_thread_flags_wait(WORKER_EVENTS_MASK, FuriFlagWaitAny, FuriWaitForever);
|
||||
furi_check((events & FuriFlagError) == 0);
|
||||
|
||||
if(events & WorkerEventStop) break;
|
||||
if(events & WorkerEventRx) {
|
||||
size_t length = 0;
|
||||
do {
|
||||
uint8_t data[64];
|
||||
length = xStreamBufferReceive(app->rx_stream, data, 64, 0);
|
||||
if(length > 0) {
|
||||
furi_hal_uart_tx(FuriHalUartIdUSART1, data, length);
|
||||
with_view_model(
|
||||
app->view, (UartDumpModel * model) {
|
||||
for(size_t i = 0; i < length; i++) {
|
||||
uart_echo_push_to_list(model, data[i]);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
} while(length > 0);
|
||||
|
||||
notification_message(app->notification, &sequence_notification);
|
||||
with_view_model(
|
||||
app->view, (UartDumpModel * model) {
|
||||
UNUSED(model);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UartEchoApp* uart_echo_app_alloc() {
|
||||
UartEchoApp* app = malloc(sizeof(UartEchoApp));
|
||||
|
||||
app->rx_stream = xStreamBufferCreate(2048, 1);
|
||||
|
||||
// Gui
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
// View dispatcher
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// Views
|
||||
app->view = view_alloc();
|
||||
view_set_draw_callback(app->view, uart_echo_view_draw_callback);
|
||||
view_set_input_callback(app->view, uart_echo_view_input_callback);
|
||||
view_allocate_model(app->view, ViewModelTypeLocking, sizeof(UartDumpModel));
|
||||
with_view_model(
|
||||
app->view, (UartDumpModel * model) {
|
||||
for(size_t i = 0; i < LINES_ON_SCREEN; i++) {
|
||||
model->line = 0;
|
||||
model->escape = false;
|
||||
model->list[i] = malloc(sizeof(ListElement));
|
||||
string_init(model->list[i]->text);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
view_set_previous_callback(app->view, uart_echo_exit);
|
||||
view_dispatcher_add_view(app->view_dispatcher, 0, app->view);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, 0);
|
||||
|
||||
// Enable uart listener
|
||||
furi_hal_console_disable();
|
||||
furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200);
|
||||
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app);
|
||||
|
||||
app->worker_thread = furi_thread_alloc();
|
||||
furi_thread_set_name(app->worker_thread, "UsbUartWorker");
|
||||
furi_thread_set_stack_size(app->worker_thread, 1024);
|
||||
furi_thread_set_context(app->worker_thread, app);
|
||||
furi_thread_set_callback(app->worker_thread, uart_echo_worker);
|
||||
furi_thread_start(app->worker_thread);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
static void uart_echo_app_free(UartEchoApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventStop);
|
||||
furi_thread_join(app->worker_thread);
|
||||
furi_thread_free(app->worker_thread);
|
||||
|
||||
furi_hal_console_enable();
|
||||
|
||||
// Free views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, 0);
|
||||
|
||||
with_view_model(
|
||||
app->view, (UartDumpModel * model) {
|
||||
for(size_t i = 0; i < LINES_ON_SCREEN; i++) {
|
||||
string_clear(model->list[i]->text);
|
||||
free(model->list[i]);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
view_free(app->view);
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
|
||||
// Close gui record
|
||||
furi_record_close(RECORD_GUI);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
app->gui = NULL;
|
||||
|
||||
vStreamBufferDelete(app->rx_stream);
|
||||
|
||||
// Free rest
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t uart_echo_app(void* p) {
|
||||
UNUSED(p);
|
||||
UartEchoApp* app = uart_echo_app_alloc();
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
uart_echo_app_free(app);
|
||||
return 0;
|
||||
}
|
18
applications/debug/unit_tests/application.fam
Normal file
18
applications/debug/unit_tests/application.fam
Normal file
@@ -0,0 +1,18 @@
|
||||
App(
|
||||
appid="unit_tests",
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
entry_point="unit_tests_on_system_start",
|
||||
cdefines=["APP_UNIT_TESTS"],
|
||||
provides=["delay_test"],
|
||||
order=100,
|
||||
)
|
||||
|
||||
App(
|
||||
appid="delay_test",
|
||||
name="Delay Test",
|
||||
apptype=FlipperAppType.SYSTEM,
|
||||
entry_point="delay_test_app",
|
||||
stack_size=1 * 1024,
|
||||
requires=["unit_tests"],
|
||||
order=110,
|
||||
)
|
@@ -0,0 +1,337 @@
|
||||
#include <furi.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
#include <toolbox/stream/stream.h>
|
||||
#include <storage/storage.h>
|
||||
#include "../minunit.h"
|
||||
|
||||
static const char* test_filetype = "Flipper Format test";
|
||||
static const uint32_t test_version = 666;
|
||||
|
||||
static const char* test_string_key = "String data";
|
||||
static const char* test_string_data = "String";
|
||||
static const char* test_string_updated_data = "New string";
|
||||
static const char* test_string_updated_2_data = "And some more";
|
||||
|
||||
static const char* test_int_key = "Int32 data";
|
||||
static const int32_t test_int_data[] = {1234, -6345, 7813, 0};
|
||||
static const int32_t test_int_updated_data[] = {-1337, 69};
|
||||
static const int32_t test_int_updated_2_data[] = {-3, -2, -1, 0, 1, 2, 3};
|
||||
|
||||
static const char* test_uint_key = "Uint32 data";
|
||||
static const uint32_t test_uint_data[] = {1234, 0, 5678, 9098, 7654321};
|
||||
static const uint32_t test_uint_updated_data[] = {8, 800, 555, 35, 35};
|
||||
static const uint32_t test_uint_updated_2_data[] = {20, 21};
|
||||
|
||||
static const char* test_float_key = "Float data";
|
||||
static const float test_float_data[] = {1.5f, 1000.0f};
|
||||
static const float test_float_updated_data[] = {1.2f};
|
||||
static const float test_float_updated_2_data[] = {0.01f, 0.0f, -51.6f};
|
||||
|
||||
static const char* test_hex_key = "Hex data";
|
||||
static const uint8_t test_hex_data[] = {0xDE, 0xAD, 0xBE};
|
||||
static const uint8_t test_hex_updated_data[] = {0xFE, 0xCA};
|
||||
static const uint8_t test_hex_updated_2_data[] = {0xCA, 0xCA, 0x05};
|
||||
|
||||
static const char* test_hex_new_key = "New Hex data";
|
||||
static const uint8_t test_hex_new_data[] = {0xFF, 0x6A, 0x91};
|
||||
|
||||
static const char* test_data_nix = "Filetype: Flipper Format test\n"
|
||||
"Version: 666\n"
|
||||
"# This is comment\n"
|
||||
"String data: String\n"
|
||||
"Int32 data: 1234 -6345 7813 0\n"
|
||||
"Uint32 data: 1234 0 5678 9098 7654321\n"
|
||||
"Float data: 1.5 1000.0\n"
|
||||
"Hex data: DE AD BE";
|
||||
|
||||
static const char* test_data_win = "Filetype: Flipper Format test\r\n"
|
||||
"Version: 666\r\n"
|
||||
"# This is comment\r\n"
|
||||
"String data: String\r\n"
|
||||
"Int32 data: 1234 -6345 7813 0\r\n"
|
||||
"Uint32 data: 1234 0 5678 9098 7654321\r\n"
|
||||
"Float data: 1.5 1000.0\r\n"
|
||||
"Hex data: DE AD BE";
|
||||
|
||||
#define ARRAY_W_COUNT(x) (x), (COUNT_OF(x))
|
||||
#define ARRAY_W_BSIZE(x) (x), (sizeof(x))
|
||||
|
||||
MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) {
|
||||
string_t tmpstr;
|
||||
uint32_t version;
|
||||
uint32_t uint32_data[COUNT_OF(test_uint_data)];
|
||||
int32_t int32_data[COUNT_OF(test_int_data)];
|
||||
float float_data[COUNT_OF(test_float_data)];
|
||||
uint8_t hex_data[COUNT_OF(test_hex_data)];
|
||||
|
||||
uint32_t count;
|
||||
|
||||
// key exist test
|
||||
size_t position_before = stream_tell(flipper_format_get_raw_stream(flipper_format));
|
||||
mu_check(flipper_format_key_exist(flipper_format, test_hex_key));
|
||||
mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
|
||||
|
||||
mu_check(!flipper_format_key_exist(flipper_format, "invalid key"));
|
||||
mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
|
||||
|
||||
// stream seek to end test
|
||||
mu_check(flipper_format_seek_to_end(flipper_format));
|
||||
mu_assert_int_eq(
|
||||
stream_size(flipper_format_get_raw_stream(flipper_format)),
|
||||
stream_tell(flipper_format_get_raw_stream(flipper_format)));
|
||||
|
||||
// key exist test
|
||||
position_before = stream_tell(flipper_format_get_raw_stream(flipper_format));
|
||||
mu_check(flipper_format_key_exist(flipper_format, test_hex_key));
|
||||
mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
|
||||
|
||||
mu_check(!flipper_format_key_exist(flipper_format, "invalid key"));
|
||||
mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
|
||||
|
||||
// rewind
|
||||
mu_check(flipper_format_rewind(flipper_format));
|
||||
|
||||
// key exist test
|
||||
position_before = stream_tell(flipper_format_get_raw_stream(flipper_format));
|
||||
mu_check(flipper_format_key_exist(flipper_format, test_hex_key));
|
||||
mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
|
||||
|
||||
mu_check(!flipper_format_key_exist(flipper_format, "invalid key"));
|
||||
mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format)));
|
||||
|
||||
// read test
|
||||
string_init(tmpstr);
|
||||
|
||||
mu_check(flipper_format_read_header(flipper_format, tmpstr, &version));
|
||||
mu_assert_string_eq(test_filetype, string_get_cstr(tmpstr));
|
||||
mu_assert_int_eq(test_version, version);
|
||||
|
||||
mu_check(flipper_format_read_string(flipper_format, test_string_key, tmpstr));
|
||||
mu_assert_string_eq(test_string_data, string_get_cstr(tmpstr));
|
||||
|
||||
mu_check(flipper_format_get_value_count(flipper_format, test_int_key, &count));
|
||||
mu_assert_int_eq(COUNT_OF(test_int_data), count);
|
||||
mu_check(flipper_format_read_int32(flipper_format, test_int_key, ARRAY_W_COUNT(int32_data)));
|
||||
mu_check(memcmp(test_int_data, ARRAY_W_BSIZE(int32_data)) == 0);
|
||||
|
||||
mu_check(flipper_format_get_value_count(flipper_format, test_uint_key, &count));
|
||||
mu_assert_int_eq(COUNT_OF(test_uint_data), count);
|
||||
mu_check(
|
||||
flipper_format_read_uint32(flipper_format, test_uint_key, ARRAY_W_COUNT(uint32_data)));
|
||||
mu_check(memcmp(test_uint_data, ARRAY_W_BSIZE(uint32_data)) == 0);
|
||||
|
||||
mu_check(flipper_format_get_value_count(flipper_format, test_float_key, &count));
|
||||
mu_assert_int_eq(COUNT_OF(test_float_data), count);
|
||||
mu_check(flipper_format_read_float(flipper_format, test_float_key, ARRAY_W_COUNT(float_data)));
|
||||
mu_check(memcmp(test_float_data, ARRAY_W_BSIZE(float_data)) == 0);
|
||||
|
||||
mu_check(flipper_format_get_value_count(flipper_format, test_hex_key, &count));
|
||||
mu_assert_int_eq(COUNT_OF(test_hex_data), count);
|
||||
mu_check(flipper_format_read_hex(flipper_format, test_hex_key, ARRAY_W_COUNT(hex_data)));
|
||||
mu_check(memcmp(test_hex_data, ARRAY_W_BSIZE(hex_data)) == 0);
|
||||
|
||||
mu_check(!flipper_format_read_string(flipper_format, "Key that doesn't exist", tmpstr));
|
||||
|
||||
string_clear(tmpstr);
|
||||
|
||||
// update data
|
||||
mu_check(flipper_format_rewind(flipper_format));
|
||||
mu_check(flipper_format_update_string_cstr(
|
||||
flipper_format, test_string_key, test_string_updated_data));
|
||||
mu_check(flipper_format_update_int32(
|
||||
flipper_format, test_int_key, ARRAY_W_COUNT(test_int_updated_data)));
|
||||
mu_check(flipper_format_update_uint32(
|
||||
flipper_format, test_uint_key, ARRAY_W_COUNT(test_uint_updated_data)));
|
||||
mu_check(flipper_format_update_float(
|
||||
flipper_format, test_float_key, ARRAY_W_COUNT(test_float_updated_data)));
|
||||
mu_check(flipper_format_update_hex(
|
||||
flipper_format, test_hex_key, ARRAY_W_COUNT(test_hex_updated_data)));
|
||||
|
||||
// read updated data test
|
||||
uint32_t uint32_updated_data[COUNT_OF(test_uint_updated_data)];
|
||||
int32_t int32_updated_data[COUNT_OF(test_int_updated_data)];
|
||||
float float_updated_data[COUNT_OF(test_float_updated_data)];
|
||||
uint8_t hex_updated_data[COUNT_OF(test_hex_updated_data)];
|
||||
|
||||
mu_check(flipper_format_rewind(flipper_format));
|
||||
string_init(tmpstr);
|
||||
|
||||
mu_check(flipper_format_read_header(flipper_format, tmpstr, &version));
|
||||
mu_assert_string_eq(test_filetype, string_get_cstr(tmpstr));
|
||||
mu_assert_int_eq(test_version, version);
|
||||
|
||||
mu_check(flipper_format_read_string(flipper_format, test_string_key, tmpstr));
|
||||
mu_assert_string_eq(test_string_updated_data, string_get_cstr(tmpstr));
|
||||
|
||||
mu_check(flipper_format_get_value_count(flipper_format, test_int_key, &count));
|
||||
mu_assert_int_eq(COUNT_OF(test_int_updated_data), count);
|
||||
mu_check(flipper_format_read_int32(
|
||||
flipper_format, test_int_key, ARRAY_W_COUNT(int32_updated_data)));
|
||||
mu_check(memcmp(test_int_updated_data, ARRAY_W_BSIZE(int32_updated_data)) == 0);
|
||||
|
||||
mu_check(flipper_format_get_value_count(flipper_format, test_uint_key, &count));
|
||||
mu_assert_int_eq(COUNT_OF(test_uint_updated_data), count);
|
||||
mu_check(flipper_format_read_uint32(
|
||||
flipper_format, test_uint_key, ARRAY_W_COUNT(uint32_updated_data)));
|
||||
mu_check(memcmp(test_uint_updated_data, ARRAY_W_BSIZE(uint32_updated_data)) == 0);
|
||||
|
||||
mu_check(flipper_format_get_value_count(flipper_format, test_float_key, &count));
|
||||
mu_assert_int_eq(COUNT_OF(test_float_updated_data), count);
|
||||
mu_check(flipper_format_read_float(
|
||||
flipper_format, test_float_key, ARRAY_W_COUNT(float_updated_data)));
|
||||
mu_check(memcmp(test_float_updated_data, ARRAY_W_BSIZE(float_updated_data)) == 0);
|
||||
|
||||
mu_check(flipper_format_get_value_count(flipper_format, test_hex_key, &count));
|
||||
mu_assert_int_eq(COUNT_OF(test_hex_updated_data), count);
|
||||
mu_check(
|
||||
flipper_format_read_hex(flipper_format, test_hex_key, ARRAY_W_COUNT(hex_updated_data)));
|
||||
mu_check(memcmp(test_hex_updated_data, ARRAY_W_BSIZE(hex_updated_data)) == 0);
|
||||
|
||||
mu_check(!flipper_format_read_string(flipper_format, "Key that doesn't exist", tmpstr));
|
||||
|
||||
string_clear(tmpstr);
|
||||
|
||||
// update data
|
||||
mu_check(flipper_format_rewind(flipper_format));
|
||||
mu_check(flipper_format_insert_or_update_string_cstr(
|
||||
flipper_format, test_string_key, test_string_updated_2_data));
|
||||
mu_check(flipper_format_insert_or_update_int32(
|
||||
flipper_format, test_int_key, ARRAY_W_COUNT(test_int_updated_2_data)));
|
||||
mu_check(flipper_format_insert_or_update_uint32(
|
||||
flipper_format, test_uint_key, ARRAY_W_COUNT(test_uint_updated_2_data)));
|
||||
mu_check(flipper_format_insert_or_update_float(
|
||||
flipper_format, test_float_key, ARRAY_W_COUNT(test_float_updated_2_data)));
|
||||
mu_check(flipper_format_insert_or_update_hex(
|
||||
flipper_format, test_hex_key, ARRAY_W_COUNT(test_hex_updated_2_data)));
|
||||
mu_check(flipper_format_insert_or_update_hex(
|
||||
flipper_format, test_hex_new_key, ARRAY_W_COUNT(test_hex_new_data)));
|
||||
|
||||
uint32_t uint32_updated_2_data[COUNT_OF(test_uint_updated_2_data)];
|
||||
int32_t int32_updated_2_data[COUNT_OF(test_int_updated_2_data)];
|
||||
float float_updated_2_data[COUNT_OF(test_float_updated_2_data)];
|
||||
uint8_t hex_updated_2_data[COUNT_OF(test_hex_updated_2_data)];
|
||||
uint8_t hex_new_data[COUNT_OF(test_hex_new_data)];
|
||||
|
||||
mu_check(flipper_format_rewind(flipper_format));
|
||||
string_init(tmpstr);
|
||||
|
||||
mu_check(flipper_format_read_header(flipper_format, tmpstr, &version));
|
||||
mu_assert_string_eq(test_filetype, string_get_cstr(tmpstr));
|
||||
mu_assert_int_eq(test_version, version);
|
||||
|
||||
mu_check(flipper_format_read_string(flipper_format, test_string_key, tmpstr));
|
||||
mu_assert_string_eq(test_string_updated_2_data, string_get_cstr(tmpstr));
|
||||
|
||||
mu_check(flipper_format_get_value_count(flipper_format, test_int_key, &count));
|
||||
mu_assert_int_eq(COUNT_OF(test_int_updated_2_data), count);
|
||||
mu_check(flipper_format_read_int32(
|
||||
flipper_format, test_int_key, ARRAY_W_COUNT(int32_updated_2_data)));
|
||||
mu_check(memcmp(test_int_updated_2_data, ARRAY_W_BSIZE(int32_updated_2_data)) == 0);
|
||||
|
||||
mu_check(flipper_format_get_value_count(flipper_format, test_uint_key, &count));
|
||||
mu_assert_int_eq(COUNT_OF(test_uint_updated_2_data), count);
|
||||
mu_check(flipper_format_read_uint32(
|
||||
flipper_format, test_uint_key, ARRAY_W_COUNT(uint32_updated_2_data)));
|
||||
mu_check(memcmp(test_uint_updated_2_data, ARRAY_W_BSIZE(uint32_updated_2_data)) == 0);
|
||||
|
||||
mu_check(flipper_format_get_value_count(flipper_format, test_float_key, &count));
|
||||
mu_assert_int_eq(COUNT_OF(test_float_updated_2_data), count);
|
||||
mu_check(flipper_format_read_float(
|
||||
flipper_format, test_float_key, ARRAY_W_COUNT(float_updated_2_data)));
|
||||
mu_check(memcmp(test_float_updated_2_data, ARRAY_W_BSIZE(float_updated_2_data)) == 0);
|
||||
|
||||
mu_check(flipper_format_get_value_count(flipper_format, test_hex_key, &count));
|
||||
mu_assert_int_eq(COUNT_OF(test_hex_updated_2_data), count);
|
||||
mu_check(
|
||||
flipper_format_read_hex(flipper_format, test_hex_key, ARRAY_W_COUNT(hex_updated_2_data)));
|
||||
mu_check(memcmp(test_hex_updated_2_data, ARRAY_W_BSIZE(hex_updated_2_data)) == 0);
|
||||
|
||||
mu_check(flipper_format_get_value_count(flipper_format, test_hex_new_key, &count));
|
||||
mu_assert_int_eq(COUNT_OF(test_hex_new_data), count);
|
||||
mu_check(
|
||||
flipper_format_read_hex(flipper_format, test_hex_new_key, ARRAY_W_COUNT(hex_new_data)));
|
||||
mu_check(memcmp(test_hex_new_data, ARRAY_W_BSIZE(hex_new_data)) == 0);
|
||||
|
||||
mu_check(!flipper_format_read_string(flipper_format, "Key that doesn't exist", tmpstr));
|
||||
|
||||
string_clear(tmpstr);
|
||||
|
||||
// delete key test
|
||||
mu_check(flipper_format_rewind(flipper_format));
|
||||
mu_check(flipper_format_delete_key(flipper_format, test_uint_key));
|
||||
|
||||
// deleted key read test
|
||||
mu_check(flipper_format_rewind(flipper_format));
|
||||
mu_check(!flipper_format_read_uint32(
|
||||
flipper_format, test_uint_key, ARRAY_W_COUNT(uint32_updated_data)));
|
||||
}
|
||||
|
||||
MU_TEST(flipper_format_string_test) {
|
||||
FlipperFormat* flipper_format = flipper_format_string_alloc();
|
||||
Stream* stream = flipper_format_get_raw_stream(flipper_format);
|
||||
|
||||
mu_check(flipper_format_write_header_cstr(flipper_format, test_filetype, test_version));
|
||||
mu_check(flipper_format_write_comment_cstr(flipper_format, "This is comment"));
|
||||
mu_check(flipper_format_write_string_cstr(flipper_format, test_string_key, test_string_data));
|
||||
mu_check(
|
||||
flipper_format_write_int32(flipper_format, test_int_key, ARRAY_W_COUNT(test_int_data)));
|
||||
mu_check(
|
||||
flipper_format_write_uint32(flipper_format, test_uint_key, ARRAY_W_COUNT(test_uint_data)));
|
||||
mu_check(flipper_format_write_float(
|
||||
flipper_format, test_float_key, ARRAY_W_COUNT(test_float_data)));
|
||||
mu_check(flipper_format_write_hex(flipper_format, test_hex_key, ARRAY_W_COUNT(test_hex_data)));
|
||||
|
||||
MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);
|
||||
|
||||
stream_clean(stream);
|
||||
stream_write_cstring(stream, test_data_nix);
|
||||
MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);
|
||||
|
||||
stream_clean(stream);
|
||||
stream_write_cstring(stream, test_data_win);
|
||||
MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);
|
||||
|
||||
flipper_format_free(flipper_format);
|
||||
}
|
||||
|
||||
MU_TEST(flipper_format_file_test) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* flipper_format = flipper_format_file_alloc(storage);
|
||||
mu_check(flipper_format_file_open_always(flipper_format, EXT_PATH("flipper.fff")));
|
||||
Stream* stream = flipper_format_get_raw_stream(flipper_format);
|
||||
|
||||
mu_check(flipper_format_write_header_cstr(flipper_format, test_filetype, test_version));
|
||||
mu_check(flipper_format_write_comment_cstr(flipper_format, "This is comment"));
|
||||
mu_check(flipper_format_write_string_cstr(flipper_format, test_string_key, test_string_data));
|
||||
mu_check(
|
||||
flipper_format_write_int32(flipper_format, test_int_key, ARRAY_W_COUNT(test_int_data)));
|
||||
mu_check(
|
||||
flipper_format_write_uint32(flipper_format, test_uint_key, ARRAY_W_COUNT(test_uint_data)));
|
||||
mu_check(flipper_format_write_float(
|
||||
flipper_format, test_float_key, ARRAY_W_COUNT(test_float_data)));
|
||||
mu_check(flipper_format_write_hex(flipper_format, test_hex_key, ARRAY_W_COUNT(test_hex_data)));
|
||||
|
||||
MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);
|
||||
|
||||
stream_clean(stream);
|
||||
stream_write_cstring(stream, test_data_nix);
|
||||
MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);
|
||||
|
||||
stream_clean(stream);
|
||||
stream_write_cstring(stream, test_data_win);
|
||||
MU_RUN_TEST_1(flipper_format_read_and_update_test, flipper_format);
|
||||
|
||||
flipper_format_free(flipper_format);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(flipper_format_string_suite) {
|
||||
MU_RUN_TEST(flipper_format_string_test);
|
||||
MU_RUN_TEST(flipper_format_file_test);
|
||||
}
|
||||
|
||||
int run_minunit_test_flipper_format_string() {
|
||||
MU_RUN_SUITE(flipper_format_string_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
@@ -0,0 +1,525 @@
|
||||
#include <furi.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
#include <toolbox/stream/stream.h>
|
||||
#include "../minunit.h"
|
||||
|
||||
#define TEST_DIR TEST_DIR_NAME "/"
|
||||
#define TEST_DIR_NAME EXT_PATH("unit_tests_tmp")
|
||||
|
||||
static const char* test_filetype = "Flipper File test";
|
||||
static const uint32_t test_version = 666;
|
||||
|
||||
static const char* test_string_key = "String data";
|
||||
static const char* test_string_data = "String";
|
||||
static const char* test_string_updated_data = "New string";
|
||||
|
||||
static const char* test_int_key = "Int32 data";
|
||||
static const int32_t test_int_data[] = {1234, -6345, 7813, 0};
|
||||
static const int32_t test_int_updated_data[] = {-1337, 69};
|
||||
|
||||
static const char* test_uint_key = "Uint32 data";
|
||||
static const uint32_t test_uint_data[] = {1234, 0, 5678, 9098, 7654321};
|
||||
static const uint32_t test_uint_updated_data[] = {8, 800, 555, 35, 35};
|
||||
|
||||
static const char* test_float_key = "Float data";
|
||||
static const float test_float_data[] = {1.5f, 1000.0f};
|
||||
static const float test_float_updated_data[] = {1.2f};
|
||||
|
||||
static const char* test_bool_key = "Bool data";
|
||||
static const bool test_bool_data[] = {true, false};
|
||||
static const bool test_bool_updated_data[] = {false, true, true};
|
||||
|
||||
static const char* test_hex_key = "Hex data";
|
||||
static const uint8_t test_hex_data[] = {0xDE, 0xAD, 0xBE};
|
||||
static const uint8_t test_hex_updated_data[] = {0xFE, 0xCA};
|
||||
|
||||
#define READ_TEST_NIX "ff_nix.test"
|
||||
static const char* test_data_nix = "Filetype: Flipper File test\n"
|
||||
"Version: 666\n"
|
||||
"# This is comment\n"
|
||||
"String data: String\n"
|
||||
"Int32 data: 1234 -6345 7813 0\n"
|
||||
"Uint32 data: 1234 0 5678 9098 7654321\n"
|
||||
"Float data: 1.5 1000.0\n"
|
||||
"Bool data: true false\n"
|
||||
"Hex data: DE AD BE";
|
||||
|
||||
#define READ_TEST_WIN "ff_win.test"
|
||||
static const char* test_data_win = "Filetype: Flipper File test\r\n"
|
||||
"Version: 666\r\n"
|
||||
"# This is comment\r\n"
|
||||
"String data: String\r\n"
|
||||
"Int32 data: 1234 -6345 7813 0\r\n"
|
||||
"Uint32 data: 1234 0 5678 9098 7654321\r\n"
|
||||
"Float data: 1.5 1000.0\r\n"
|
||||
"Bool data: true false\r\n"
|
||||
"Hex data: DE AD BE";
|
||||
|
||||
#define READ_TEST_FLP "ff_flp.test"
|
||||
|
||||
// data created by user on linux machine
|
||||
static const char* test_file_linux = TEST_DIR READ_TEST_NIX;
|
||||
// data created by user on windows machine
|
||||
static const char* test_file_windows = TEST_DIR READ_TEST_WIN;
|
||||
// data created by flipper itself
|
||||
static const char* test_file_flipper = TEST_DIR READ_TEST_FLP;
|
||||
|
||||
static bool storage_write_string(const char* path, const char* data) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(storage);
|
||||
bool result = false;
|
||||
|
||||
do {
|
||||
if(!storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) break;
|
||||
if(storage_file_write(file, data, strlen(data)) != strlen(data)) break;
|
||||
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
storage_file_close(file);
|
||||
storage_file_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void tests_setup() {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
mu_assert(storage_simply_remove_recursive(storage, TEST_DIR_NAME), "Cannot clean data");
|
||||
mu_assert(storage_simply_mkdir(storage, TEST_DIR_NAME), "Cannot create dir");
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void tests_teardown() {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
mu_assert(storage_simply_remove_recursive(storage, TEST_DIR_NAME), "Cannot clean data");
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static bool test_read(const char* file_name) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
bool result = false;
|
||||
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
string_t string_value;
|
||||
string_init(string_value);
|
||||
uint32_t uint32_value;
|
||||
void* scratchpad = malloc(512);
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(file, file_name)) break;
|
||||
|
||||
if(!flipper_format_read_header(file, string_value, &uint32_value)) break;
|
||||
if(string_cmp_str(string_value, test_filetype) != 0) break;
|
||||
if(uint32_value != test_version) break;
|
||||
|
||||
if(!flipper_format_read_string(file, test_string_key, string_value)) break;
|
||||
if(string_cmp_str(string_value, test_string_data) != 0) break;
|
||||
|
||||
if(!flipper_format_get_value_count(file, test_int_key, &uint32_value)) break;
|
||||
if(uint32_value != COUNT_OF(test_int_data)) break;
|
||||
if(!flipper_format_read_int32(file, test_int_key, scratchpad, uint32_value)) break;
|
||||
if(memcmp(scratchpad, test_int_data, sizeof(int32_t) * COUNT_OF(test_int_data)) != 0)
|
||||
break;
|
||||
|
||||
if(!flipper_format_get_value_count(file, test_uint_key, &uint32_value)) break;
|
||||
if(uint32_value != COUNT_OF(test_uint_data)) break;
|
||||
if(!flipper_format_read_uint32(file, test_uint_key, scratchpad, uint32_value)) break;
|
||||
if(memcmp(scratchpad, test_uint_data, sizeof(uint32_t) * COUNT_OF(test_uint_data)) != 0)
|
||||
break;
|
||||
|
||||
if(!flipper_format_get_value_count(file, test_float_key, &uint32_value)) break;
|
||||
if(uint32_value != COUNT_OF(test_float_data)) break;
|
||||
if(!flipper_format_read_float(file, test_float_key, scratchpad, uint32_value)) break;
|
||||
if(memcmp(scratchpad, test_float_data, sizeof(float) * COUNT_OF(test_float_data)) != 0)
|
||||
break;
|
||||
|
||||
if(!flipper_format_get_value_count(file, test_bool_key, &uint32_value)) break;
|
||||
if(uint32_value != COUNT_OF(test_bool_data)) break;
|
||||
if(!flipper_format_read_bool(file, test_bool_key, scratchpad, uint32_value)) break;
|
||||
if(memcmp(scratchpad, test_bool_data, sizeof(bool) * COUNT_OF(test_bool_data)) != 0) break;
|
||||
|
||||
if(!flipper_format_get_value_count(file, test_hex_key, &uint32_value)) break;
|
||||
if(uint32_value != COUNT_OF(test_hex_data)) break;
|
||||
if(!flipper_format_read_hex(file, test_hex_key, scratchpad, uint32_value)) break;
|
||||
if(memcmp(scratchpad, test_hex_data, sizeof(uint8_t) * COUNT_OF(test_hex_data)) != 0)
|
||||
break;
|
||||
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
free(scratchpad);
|
||||
string_clear(string_value);
|
||||
|
||||
flipper_format_free(file);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool test_read_updated(const char* file_name) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
bool result = false;
|
||||
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
string_t string_value;
|
||||
string_init(string_value);
|
||||
uint32_t uint32_value;
|
||||
void* scratchpad = malloc(512);
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(file, file_name)) break;
|
||||
|
||||
if(!flipper_format_read_header(file, string_value, &uint32_value)) break;
|
||||
if(string_cmp_str(string_value, test_filetype) != 0) break;
|
||||
if(uint32_value != test_version) break;
|
||||
|
||||
if(!flipper_format_read_string(file, test_string_key, string_value)) break;
|
||||
if(string_cmp_str(string_value, test_string_updated_data) != 0) break;
|
||||
|
||||
if(!flipper_format_get_value_count(file, test_int_key, &uint32_value)) break;
|
||||
if(uint32_value != COUNT_OF(test_int_updated_data)) break;
|
||||
if(!flipper_format_read_int32(file, test_int_key, scratchpad, uint32_value)) break;
|
||||
if(memcmp(
|
||||
scratchpad,
|
||||
test_int_updated_data,
|
||||
sizeof(int32_t) * COUNT_OF(test_int_updated_data)) != 0)
|
||||
break;
|
||||
|
||||
if(!flipper_format_get_value_count(file, test_uint_key, &uint32_value)) break;
|
||||
if(uint32_value != COUNT_OF(test_uint_updated_data)) break;
|
||||
if(!flipper_format_read_uint32(file, test_uint_key, scratchpad, uint32_value)) break;
|
||||
if(memcmp(
|
||||
scratchpad,
|
||||
test_uint_updated_data,
|
||||
sizeof(uint32_t) * COUNT_OF(test_uint_updated_data)) != 0)
|
||||
break;
|
||||
|
||||
if(!flipper_format_get_value_count(file, test_float_key, &uint32_value)) break;
|
||||
if(uint32_value != COUNT_OF(test_float_updated_data)) break;
|
||||
if(!flipper_format_read_float(file, test_float_key, scratchpad, uint32_value)) break;
|
||||
if(memcmp(
|
||||
scratchpad,
|
||||
test_float_updated_data,
|
||||
sizeof(float) * COUNT_OF(test_float_updated_data)) != 0)
|
||||
break;
|
||||
|
||||
if(!flipper_format_get_value_count(file, test_bool_key, &uint32_value)) break;
|
||||
if(uint32_value != COUNT_OF(test_bool_updated_data)) break;
|
||||
if(!flipper_format_read_bool(file, test_bool_key, scratchpad, uint32_value)) break;
|
||||
if(memcmp(
|
||||
scratchpad,
|
||||
test_bool_updated_data,
|
||||
sizeof(bool) * COUNT_OF(test_bool_updated_data)) != 0)
|
||||
break;
|
||||
|
||||
if(!flipper_format_get_value_count(file, test_hex_key, &uint32_value)) break;
|
||||
if(uint32_value != COUNT_OF(test_hex_updated_data)) break;
|
||||
if(!flipper_format_read_hex(file, test_hex_key, scratchpad, uint32_value)) break;
|
||||
if(memcmp(
|
||||
scratchpad,
|
||||
test_hex_updated_data,
|
||||
sizeof(uint8_t) * COUNT_OF(test_hex_updated_data)) != 0)
|
||||
break;
|
||||
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
free(scratchpad);
|
||||
string_clear(string_value);
|
||||
|
||||
flipper_format_free(file);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool test_write(const char* file_name) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
bool result = false;
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_always(file, file_name)) break;
|
||||
if(!flipper_format_write_header_cstr(file, test_filetype, test_version)) break;
|
||||
if(!flipper_format_write_comment_cstr(file, "This is comment")) break;
|
||||
if(!flipper_format_write_string_cstr(file, test_string_key, test_string_data)) break;
|
||||
if(!flipper_format_write_int32(file, test_int_key, test_int_data, COUNT_OF(test_int_data)))
|
||||
break;
|
||||
if(!flipper_format_write_uint32(
|
||||
file, test_uint_key, test_uint_data, COUNT_OF(test_uint_data)))
|
||||
break;
|
||||
if(!flipper_format_write_float(
|
||||
file, test_float_key, test_float_data, COUNT_OF(test_float_data)))
|
||||
break;
|
||||
if(!flipper_format_write_bool(
|
||||
file, test_bool_key, test_bool_data, COUNT_OF(test_bool_data)))
|
||||
break;
|
||||
if(!flipper_format_write_hex(file, test_hex_key, test_hex_data, COUNT_OF(test_hex_data)))
|
||||
break;
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
flipper_format_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool test_delete_last_key(const char* file_name) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
bool result = false;
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(file, file_name)) break;
|
||||
if(!flipper_format_delete_key(file, test_hex_key)) break;
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
flipper_format_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool test_append_key(const char* file_name) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
bool result = false;
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_append(file, file_name)) break;
|
||||
if(!flipper_format_write_hex(file, test_hex_key, test_hex_data, COUNT_OF(test_hex_data)))
|
||||
break;
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
flipper_format_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool test_update(const char* file_name) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
bool result = false;
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(file, file_name)) break;
|
||||
if(!flipper_format_update_string_cstr(file, test_string_key, test_string_updated_data))
|
||||
break;
|
||||
if(!flipper_format_update_int32(
|
||||
file, test_int_key, test_int_updated_data, COUNT_OF(test_int_updated_data)))
|
||||
break;
|
||||
if(!flipper_format_update_uint32(
|
||||
file, test_uint_key, test_uint_updated_data, COUNT_OF(test_uint_updated_data)))
|
||||
break;
|
||||
if(!flipper_format_update_float(
|
||||
file, test_float_key, test_float_updated_data, COUNT_OF(test_float_updated_data)))
|
||||
break;
|
||||
if(!flipper_format_update_bool(
|
||||
file, test_bool_key, test_bool_updated_data, COUNT_OF(test_bool_updated_data)))
|
||||
break;
|
||||
if(!flipper_format_update_hex(
|
||||
file, test_hex_key, test_hex_updated_data, COUNT_OF(test_hex_updated_data)))
|
||||
break;
|
||||
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
flipper_format_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool test_update_backward(const char* file_name) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
bool result = false;
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(file, file_name)) break;
|
||||
if(!flipper_format_update_string_cstr(file, test_string_key, test_string_data)) break;
|
||||
if(!flipper_format_update_int32(file, test_int_key, test_int_data, COUNT_OF(test_int_data)))
|
||||
break;
|
||||
if(!flipper_format_update_uint32(
|
||||
file, test_uint_key, test_uint_data, COUNT_OF(test_uint_data)))
|
||||
break;
|
||||
if(!flipper_format_update_float(
|
||||
file, test_float_key, test_float_data, COUNT_OF(test_float_data)))
|
||||
break;
|
||||
if(!flipper_format_update_bool(
|
||||
file, test_bool_key, test_bool_data, COUNT_OF(test_bool_data)))
|
||||
break;
|
||||
if(!flipper_format_update_hex(file, test_hex_key, test_hex_data, COUNT_OF(test_hex_data)))
|
||||
break;
|
||||
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
flipper_format_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool test_write_multikey(const char* file_name) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
bool result = false;
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_always(file, file_name)) break;
|
||||
if(!flipper_format_write_header_cstr(file, test_filetype, test_version)) break;
|
||||
|
||||
bool error = false;
|
||||
for(uint8_t index = 0; index < 100; index++) {
|
||||
if(!flipper_format_write_hex(file, test_hex_key, &index, 1)) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(error) break;
|
||||
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
flipper_format_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool test_read_multikey(const char* file_name) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
bool result = false;
|
||||
FlipperFormat* file = flipper_format_file_alloc(storage);
|
||||
|
||||
string_t string_value;
|
||||
string_init(string_value);
|
||||
uint32_t uint32_value;
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(file, file_name)) break;
|
||||
if(!flipper_format_read_header(file, string_value, &uint32_value)) break;
|
||||
if(string_cmp_str(string_value, test_filetype) != 0) break;
|
||||
if(uint32_value != test_version) break;
|
||||
|
||||
bool error = false;
|
||||
uint8_t uint8_value;
|
||||
for(uint8_t index = 0; index < 100; index++) {
|
||||
if(!flipper_format_read_hex(file, test_hex_key, &uint8_value, 1)) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(uint8_value != index) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(error) break;
|
||||
|
||||
result = true;
|
||||
} while(false);
|
||||
|
||||
string_clear(string_value);
|
||||
|
||||
flipper_format_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
MU_TEST(flipper_format_write_test) {
|
||||
mu_assert(storage_write_string(test_file_linux, test_data_nix), "Write test error [Linux]");
|
||||
mu_assert(
|
||||
storage_write_string(test_file_windows, test_data_win), "Write test error [Windows]");
|
||||
mu_assert(test_write(test_file_flipper), "Write test error [Flipper]");
|
||||
}
|
||||
|
||||
MU_TEST(flipper_format_read_test) {
|
||||
mu_assert(test_read(test_file_linux), "Read test error [Linux]");
|
||||
mu_assert(test_read(test_file_windows), "Read test error [Windows]");
|
||||
mu_assert(test_read(test_file_flipper), "Read test error [Flipper]");
|
||||
}
|
||||
|
||||
MU_TEST(flipper_format_delete_test) {
|
||||
mu_assert(test_delete_last_key(test_file_linux), "Cannot delete key [Linux]");
|
||||
mu_assert(test_delete_last_key(test_file_windows), "Cannot delete key [Windows]");
|
||||
mu_assert(test_delete_last_key(test_file_flipper), "Cannot delete key [Flipper]");
|
||||
}
|
||||
|
||||
MU_TEST(flipper_format_delete_result_test) {
|
||||
mu_assert(!test_read(test_file_linux), "Key deleted incorrectly [Linux]");
|
||||
mu_assert(!test_read(test_file_windows), "Key deleted incorrectly [Windows]");
|
||||
mu_assert(!test_read(test_file_flipper), "Key deleted incorrectly [Flipper]");
|
||||
}
|
||||
|
||||
MU_TEST(flipper_format_append_test) {
|
||||
mu_assert(test_append_key(test_file_linux), "Cannot append data [Linux]");
|
||||
mu_assert(test_append_key(test_file_windows), "Cannot append data [Windows]");
|
||||
mu_assert(test_append_key(test_file_flipper), "Cannot append data [Flipper]");
|
||||
}
|
||||
|
||||
MU_TEST(flipper_format_append_result_test) {
|
||||
mu_assert(test_read(test_file_linux), "Data appended incorrectly [Linux]");
|
||||
mu_assert(test_read(test_file_windows), "Data appended incorrectly [Windows]");
|
||||
mu_assert(test_read(test_file_flipper), "Data appended incorrectly [Flipper]");
|
||||
}
|
||||
|
||||
MU_TEST(flipper_format_update_1_test) {
|
||||
mu_assert(test_update(test_file_linux), "Cannot update data #1 [Linux]");
|
||||
mu_assert(test_update(test_file_windows), "Cannot update data #1 [Windows]");
|
||||
mu_assert(test_update(test_file_flipper), "Cannot update data #1 [Flipper]");
|
||||
}
|
||||
|
||||
MU_TEST(flipper_format_update_1_result_test) {
|
||||
mu_assert(test_read_updated(test_file_linux), "Data #1 updated incorrectly [Linux]");
|
||||
mu_assert(test_read_updated(test_file_windows), "Data #1 updated incorrectly [Windows]");
|
||||
mu_assert(test_read_updated(test_file_flipper), "Data #1 updated incorrectly [Flipper]");
|
||||
}
|
||||
|
||||
MU_TEST(flipper_format_update_2_test) {
|
||||
mu_assert(test_update_backward(test_file_linux), "Cannot update data #2 [Linux]");
|
||||
mu_assert(test_update_backward(test_file_windows), "Cannot update data #2 [Windows]");
|
||||
mu_assert(test_update_backward(test_file_flipper), "Cannot update data #2 [Flipper]");
|
||||
}
|
||||
|
||||
MU_TEST(flipper_format_update_2_result_test) {
|
||||
mu_assert(test_read(test_file_linux), "Data #2 updated incorrectly [Linux]");
|
||||
mu_assert(test_read(test_file_windows), "Data #2 updated incorrectly [Windows]");
|
||||
mu_assert(test_read(test_file_flipper), "Data #2 updated incorrectly [Flipper]");
|
||||
}
|
||||
|
||||
MU_TEST(flipper_format_multikey_test) {
|
||||
mu_assert(test_write_multikey(TEST_DIR "ff_multiline.test"), "Multikey write test error");
|
||||
mu_assert(test_read_multikey(TEST_DIR "ff_multiline.test"), "Multikey read test error");
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(flipper_format) {
|
||||
tests_setup();
|
||||
MU_RUN_TEST(flipper_format_write_test);
|
||||
MU_RUN_TEST(flipper_format_read_test);
|
||||
MU_RUN_TEST(flipper_format_delete_test);
|
||||
MU_RUN_TEST(flipper_format_delete_result_test);
|
||||
MU_RUN_TEST(flipper_format_append_test);
|
||||
MU_RUN_TEST(flipper_format_append_result_test);
|
||||
MU_RUN_TEST(flipper_format_update_1_test);
|
||||
MU_RUN_TEST(flipper_format_update_1_result_test);
|
||||
MU_RUN_TEST(flipper_format_update_2_test);
|
||||
MU_RUN_TEST(flipper_format_update_2_result_test);
|
||||
MU_RUN_TEST(flipper_format_multikey_test);
|
||||
tests_teardown();
|
||||
}
|
||||
|
||||
int run_minunit_test_flipper_format() {
|
||||
MU_RUN_SUITE(flipper_format);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
100
applications/debug/unit_tests/furi/furi_memmgr_test.c
Normal file
100
applications/debug/unit_tests/furi/furi_memmgr_test.c
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "../minunit.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// this test is not accurate, but gives a basic understanding
|
||||
// that memory management is working fine
|
||||
|
||||
// do not include memmgr.h here
|
||||
// we also test that we are linking against stdlib
|
||||
extern size_t memmgr_get_free_heap(void);
|
||||
extern size_t memmgr_get_minimum_free_heap(void);
|
||||
|
||||
// current heap managment realization consume:
|
||||
// X bytes after allocate and 0 bytes after allocate and free,
|
||||
// where X = sizeof(void*) + sizeof(size_t), look to BlockLink_t
|
||||
const size_t heap_overhead_max_size = sizeof(void*) + sizeof(size_t);
|
||||
|
||||
bool heap_equal(size_t heap_size, size_t heap_size_old) {
|
||||
// heap borders with overhead
|
||||
const size_t heap_low = heap_size_old - heap_overhead_max_size;
|
||||
const size_t heap_high = heap_size_old + heap_overhead_max_size;
|
||||
|
||||
// not extact, so we must test it against bigger numbers than "overhead size"
|
||||
const bool result = ((heap_size >= heap_low) && (heap_size <= heap_high));
|
||||
|
||||
// debug allocation info
|
||||
if(!result) {
|
||||
printf("\n(hl: %zu) <= (p: %zu) <= (hh: %zu)\n", heap_low, heap_size, heap_high);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void test_furi_memmgr() {
|
||||
size_t heap_size = 0;
|
||||
size_t heap_size_old = 0;
|
||||
const int alloc_size = 128;
|
||||
|
||||
void* ptr = NULL;
|
||||
void* original_ptr = NULL;
|
||||
|
||||
// do not include furi memmgr.h case
|
||||
#ifdef FURI_MEMMGR_GUARD
|
||||
mu_fail("do not link against furi memmgr.h");
|
||||
#endif
|
||||
|
||||
// allocate memory case
|
||||
heap_size_old = memmgr_get_free_heap();
|
||||
ptr = malloc(alloc_size);
|
||||
heap_size = memmgr_get_free_heap();
|
||||
mu_assert_pointers_not_eq(ptr, NULL);
|
||||
mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "allocate failed");
|
||||
|
||||
// free memory case
|
||||
heap_size_old = memmgr_get_free_heap();
|
||||
free(ptr);
|
||||
ptr = NULL;
|
||||
heap_size = memmgr_get_free_heap();
|
||||
mu_assert(heap_equal(heap_size, heap_size_old + alloc_size), "free failed");
|
||||
|
||||
// reallocate memory case
|
||||
|
||||
// get filled array with some data
|
||||
original_ptr = malloc(alloc_size);
|
||||
mu_assert_pointers_not_eq(original_ptr, NULL);
|
||||
for(int i = 0; i < alloc_size; i++) {
|
||||
*(unsigned char*)(original_ptr + i) = i;
|
||||
}
|
||||
|
||||
// malloc array and copy data
|
||||
ptr = malloc(alloc_size);
|
||||
mu_assert_pointers_not_eq(ptr, NULL);
|
||||
memcpy(ptr, original_ptr, alloc_size);
|
||||
|
||||
// reallocate array
|
||||
heap_size_old = memmgr_get_free_heap();
|
||||
ptr = realloc(ptr, alloc_size * 2);
|
||||
heap_size = memmgr_get_free_heap();
|
||||
mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "reallocate failed");
|
||||
mu_assert_int_eq(memcmp(original_ptr, ptr, alloc_size), 0);
|
||||
free(original_ptr);
|
||||
free(ptr);
|
||||
|
||||
// allocate and zero-initialize array (calloc)
|
||||
original_ptr = malloc(alloc_size);
|
||||
mu_assert_pointers_not_eq(original_ptr, NULL);
|
||||
|
||||
for(int i = 0; i < alloc_size; i++) {
|
||||
*(unsigned char*)(original_ptr + i) = 0;
|
||||
}
|
||||
heap_size_old = memmgr_get_free_heap();
|
||||
ptr = calloc(1, alloc_size);
|
||||
heap_size = memmgr_get_free_heap();
|
||||
mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "callocate failed");
|
||||
mu_assert_int_eq(memcmp(original_ptr, ptr, alloc_size), 0);
|
||||
|
||||
free(original_ptr);
|
||||
free(ptr);
|
||||
}
|
45
applications/debug/unit_tests/furi/furi_pubsub_test.c
Normal file
45
applications/debug/unit_tests/furi/furi_pubsub_test.c
Normal file
@@ -0,0 +1,45 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <furi.h>
|
||||
#include "../minunit.h"
|
||||
|
||||
const uint32_t context_value = 0xdeadbeef;
|
||||
const uint32_t notify_value_0 = 0x12345678;
|
||||
const uint32_t notify_value_1 = 0x11223344;
|
||||
|
||||
uint32_t pubsub_value = 0;
|
||||
uint32_t pubsub_context_value = 0;
|
||||
|
||||
void test_pubsub_handler(const void* arg, void* ctx) {
|
||||
pubsub_value = *(uint32_t*)arg;
|
||||
pubsub_context_value = *(uint32_t*)ctx;
|
||||
}
|
||||
|
||||
void test_furi_pubsub() {
|
||||
FuriPubSub* test_pubsub = NULL;
|
||||
FuriPubSubSubscription* test_pubsub_subscription = NULL;
|
||||
|
||||
// init pubsub case
|
||||
test_pubsub = furi_pubsub_alloc();
|
||||
mu_assert_pointers_not_eq(test_pubsub, NULL);
|
||||
|
||||
// subscribe pubsub case
|
||||
test_pubsub_subscription =
|
||||
furi_pubsub_subscribe(test_pubsub, test_pubsub_handler, (void*)&context_value);
|
||||
mu_assert_pointers_not_eq(test_pubsub_subscription, NULL);
|
||||
|
||||
/// notify pubsub case
|
||||
furi_pubsub_publish(test_pubsub, (void*)¬ify_value_0);
|
||||
mu_assert_int_eq(pubsub_value, notify_value_0);
|
||||
mu_assert_int_eq(pubsub_context_value, context_value);
|
||||
|
||||
// unsubscribe pubsub case
|
||||
furi_pubsub_unsubscribe(test_pubsub, test_pubsub_subscription);
|
||||
|
||||
/// notify unsubscribed pubsub case
|
||||
furi_pubsub_publish(test_pubsub, (void*)¬ify_value_1);
|
||||
mu_assert_int_not_eq(pubsub_value, notify_value_1);
|
||||
|
||||
// delete pubsub case
|
||||
furi_pubsub_free(test_pubsub);
|
||||
}
|
20
applications/debug/unit_tests/furi/furi_record_test.c
Normal file
20
applications/debug/unit_tests/furi/furi_record_test.c
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <furi.h>
|
||||
#include "../minunit.h"
|
||||
|
||||
void test_furi_create_open() {
|
||||
// 1. Create record
|
||||
uint8_t test_data = 0;
|
||||
furi_record_create("test/holding", (void*)&test_data);
|
||||
|
||||
// 2. Open it
|
||||
void* record = furi_record_open("test/holding");
|
||||
mu_assert_pointers_eq(record, &test_data);
|
||||
|
||||
// 3. Close it
|
||||
furi_record_close("test/holding");
|
||||
|
||||
// 4. Clean up
|
||||
furi_record_destroy("test/holding");
|
||||
}
|
63
applications/debug/unit_tests/furi/furi_test.c
Normal file
63
applications/debug/unit_tests/furi/furi_test.c
Normal file
@@ -0,0 +1,63 @@
|
||||
#include <stdio.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../minunit.h"
|
||||
|
||||
// v2 tests
|
||||
void test_furi_create_open();
|
||||
void test_furi_valuemutex();
|
||||
void test_furi_concurrent_access();
|
||||
void test_furi_pubsub();
|
||||
|
||||
void test_furi_memmgr();
|
||||
|
||||
static int foo = 0;
|
||||
|
||||
void test_setup(void) {
|
||||
foo = 7;
|
||||
}
|
||||
|
||||
void test_teardown(void) {
|
||||
/* Nothing */
|
||||
}
|
||||
|
||||
MU_TEST(test_check) {
|
||||
mu_check(foo != 6);
|
||||
}
|
||||
|
||||
// v2 tests
|
||||
MU_TEST(mu_test_furi_create_open) {
|
||||
test_furi_create_open();
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_valuemutex) {
|
||||
test_furi_valuemutex();
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_pubsub) {
|
||||
test_furi_pubsub();
|
||||
}
|
||||
|
||||
MU_TEST(mu_test_furi_memmgr) {
|
||||
// this test is not accurate, but gives a basic understanding
|
||||
// that memory management is working fine
|
||||
test_furi_memmgr();
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(test_suite) {
|
||||
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
|
||||
|
||||
MU_RUN_TEST(test_check);
|
||||
|
||||
// v2 tests
|
||||
MU_RUN_TEST(mu_test_furi_create_open);
|
||||
MU_RUN_TEST(mu_test_furi_valuemutex);
|
||||
MU_RUN_TEST(mu_test_furi_pubsub);
|
||||
MU_RUN_TEST(mu_test_furi_memmgr);
|
||||
}
|
||||
|
||||
int run_minunit_test_furi() {
|
||||
MU_RUN_SUITE(test_suite);
|
||||
|
||||
return MU_EXIT_CODE;
|
||||
}
|
41
applications/debug/unit_tests/furi/furi_valuemutex_test.c
Normal file
41
applications/debug/unit_tests/furi/furi_valuemutex_test.c
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <furi.h>
|
||||
|
||||
#include "../minunit.h"
|
||||
|
||||
void test_furi_valuemutex() {
|
||||
const int init_value = 0xdeadbeef;
|
||||
const int changed_value = 0x12345678;
|
||||
|
||||
int value = init_value;
|
||||
bool result;
|
||||
ValueMutex valuemutex;
|
||||
|
||||
// init mutex case
|
||||
result = init_mutex(&valuemutex, &value, sizeof(value));
|
||||
mu_assert(result, "init mutex failed");
|
||||
|
||||
// acquire mutex case
|
||||
int* value_pointer = acquire_mutex(&valuemutex, 100);
|
||||
mu_assert_pointers_eq(value_pointer, &value);
|
||||
|
||||
// second acquire mutex case
|
||||
int* value_pointer_second = acquire_mutex(&valuemutex, 100);
|
||||
mu_assert_pointers_eq(value_pointer_second, NULL);
|
||||
|
||||
// change value case
|
||||
*value_pointer = changed_value;
|
||||
mu_assert_int_eq(value, changed_value);
|
||||
|
||||
// release mutex case
|
||||
result = release_mutex(&valuemutex, &value);
|
||||
mu_assert(result, "release mutex failed");
|
||||
|
||||
// TODO
|
||||
//acquire mutex blocking case
|
||||
//write mutex blocking case
|
||||
//read mutex blocking case
|
||||
|
||||
mu_check(delete_mutex(&valuemutex));
|
||||
}
|
521
applications/debug/unit_tests/infrared/infrared_test.c
Normal file
521
applications/debug/unit_tests/infrared/infrared_test.c
Normal file
@@ -0,0 +1,521 @@
|
||||
#include <furi.h>
|
||||
#include <flipper_format.h>
|
||||
#include <infrared.h>
|
||||
#include <common/infrared_common_i.h>
|
||||
#include "../minunit.h"
|
||||
|
||||
#define IR_TEST_FILES_DIR EXT_PATH("unit_tests/infrared/")
|
||||
#define IR_TEST_FILE_PREFIX "test_"
|
||||
#define IR_TEST_FILE_SUFFIX ".irtest"
|
||||
|
||||
typedef struct {
|
||||
InfraredDecoderHandler* decoder_handler;
|
||||
InfraredEncoderHandler* encoder_handler;
|
||||
string_t file_path;
|
||||
FlipperFormat* ff;
|
||||
} InfraredTest;
|
||||
|
||||
static InfraredTest* test;
|
||||
|
||||
static void infrared_test_alloc() {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
test = malloc(sizeof(InfraredTest));
|
||||
test->decoder_handler = infrared_alloc_decoder();
|
||||
test->encoder_handler = infrared_alloc_encoder();
|
||||
test->ff = flipper_format_buffered_file_alloc(storage);
|
||||
string_init(test->file_path);
|
||||
}
|
||||
|
||||
static void infrared_test_free() {
|
||||
furi_assert(test);
|
||||
infrared_free_decoder(test->decoder_handler);
|
||||
infrared_free_encoder(test->encoder_handler);
|
||||
flipper_format_free(test->ff);
|
||||
string_clear(test->file_path);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
free(test);
|
||||
test = NULL;
|
||||
}
|
||||
|
||||
static bool infrared_test_prepare_file(const char* protocol_name) {
|
||||
string_t file_type;
|
||||
string_init(file_type);
|
||||
bool success = false;
|
||||
|
||||
string_printf(
|
||||
test->file_path,
|
||||
"%s%s%s%s",
|
||||
IR_TEST_FILES_DIR,
|
||||
IR_TEST_FILE_PREFIX,
|
||||
protocol_name,
|
||||
IR_TEST_FILE_SUFFIX);
|
||||
|
||||
do {
|
||||
uint32_t format_version;
|
||||
if(!flipper_format_buffered_file_open_existing(test->ff, string_get_cstr(test->file_path)))
|
||||
break;
|
||||
if(!flipper_format_read_header(test->ff, file_type, &format_version)) break;
|
||||
if(string_cmp_str(file_type, "IR tests file") || format_version != 1) break;
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
string_clear(file_type);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool infrared_test_load_raw_signal(
|
||||
FlipperFormat* ff,
|
||||
const char* signal_name,
|
||||
uint32_t** timings,
|
||||
uint32_t* timings_count) {
|
||||
string_t buf;
|
||||
string_init(buf);
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
bool is_name_found = false;
|
||||
for(; !is_name_found && flipper_format_read_string(ff, "name", buf);
|
||||
is_name_found = !string_cmp_str(buf, signal_name))
|
||||
;
|
||||
|
||||
if(!is_name_found) break;
|
||||
if(!flipper_format_read_string(ff, "type", buf) || string_cmp_str(buf, "raw")) break;
|
||||
if(!flipper_format_get_value_count(ff, "data", timings_count)) break;
|
||||
if(!*timings_count) break;
|
||||
|
||||
*timings = malloc(*timings_count * sizeof(uint32_t*));
|
||||
if(!flipper_format_read_uint32(ff, "data", *timings, *timings_count)) {
|
||||
free(*timings);
|
||||
break;
|
||||
}
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
string_clear(buf);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool infrared_test_read_message(FlipperFormat* ff, InfraredMessage* message) {
|
||||
string_t buf;
|
||||
string_init(buf);
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
if(!flipper_format_read_string(ff, "protocol", buf)) break;
|
||||
message->protocol = infrared_get_protocol_by_name(string_get_cstr(buf));
|
||||
if(!infrared_is_protocol_valid(message->protocol)) break;
|
||||
if(!flipper_format_read_hex(ff, "address", (uint8_t*)&message->address, sizeof(uint32_t)))
|
||||
break;
|
||||
if(!flipper_format_read_hex(ff, "command", (uint8_t*)&message->command, sizeof(uint32_t)))
|
||||
break;
|
||||
if(!flipper_format_read_bool(ff, "repeat", &message->repeat, 1)) break;
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
string_clear(buf);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool infrared_test_load_messages(
|
||||
FlipperFormat* ff,
|
||||
const char* signal_name,
|
||||
InfraredMessage** messages,
|
||||
uint32_t* messages_count) {
|
||||
string_t buf;
|
||||
string_init(buf);
|
||||
bool success = false;
|
||||
|
||||
do {
|
||||
bool is_name_found = false;
|
||||
for(; !is_name_found && flipper_format_read_string(ff, "name", buf);
|
||||
is_name_found = !string_cmp_str(buf, signal_name))
|
||||
;
|
||||
|
||||
if(!is_name_found) break;
|
||||
if(!flipper_format_read_string(ff, "type", buf) || string_cmp_str(buf, "parsed_array"))
|
||||
break;
|
||||
if(!flipper_format_read_uint32(ff, "count", messages_count, 1)) break;
|
||||
if(!*messages_count) break;
|
||||
|
||||
*messages = malloc(*messages_count * sizeof(InfraredMessage));
|
||||
uint32_t i;
|
||||
for(i = 0; i < *messages_count; ++i) {
|
||||
if(!infrared_test_read_message(ff, (*messages) + i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(*messages_count != i) {
|
||||
free(*messages);
|
||||
break;
|
||||
}
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
string_clear(buf);
|
||||
return success;
|
||||
}
|
||||
|
||||
static void infrared_test_compare_message_results(
|
||||
const InfraredMessage* message_decoded,
|
||||
const InfraredMessage* message_expected) {
|
||||
mu_check(message_decoded->protocol == message_expected->protocol);
|
||||
mu_check(message_decoded->command == message_expected->command);
|
||||
mu_check(message_decoded->address == message_expected->address);
|
||||
if((message_expected->protocol == InfraredProtocolSIRC) ||
|
||||
(message_expected->protocol == InfraredProtocolSIRC15) ||
|
||||
(message_expected->protocol == InfraredProtocolSIRC20)) {
|
||||
mu_check(message_decoded->repeat == false);
|
||||
} else {
|
||||
mu_check(message_decoded->repeat == message_expected->repeat);
|
||||
}
|
||||
}
|
||||
|
||||
/* Encodes signal and merges same levels (high+high, low+low) */
|
||||
static void infrared_test_run_encoder_fill_array(
|
||||
InfraredEncoderHandler* handler,
|
||||
uint32_t* timings,
|
||||
uint32_t* timings_len,
|
||||
bool* start_level) {
|
||||
uint32_t duration = 0;
|
||||
bool level = false;
|
||||
bool level_read;
|
||||
InfraredStatus status = InfraredStatusError;
|
||||
size_t i = 0;
|
||||
bool first = true;
|
||||
|
||||
while(1) {
|
||||
status = infrared_encode(handler, &duration, &level_read);
|
||||
if(first) {
|
||||
if(start_level) *start_level = level_read;
|
||||
first = false;
|
||||
timings[0] = 0;
|
||||
} else if(level_read != level) {
|
||||
++i;
|
||||
furi_check(i < *timings_len);
|
||||
timings[i] = 0;
|
||||
}
|
||||
level = level_read;
|
||||
timings[i] += duration;
|
||||
|
||||
furi_check((status == InfraredStatusOk) || (status == InfraredStatusDone));
|
||||
if(status == InfraredStatusDone) break;
|
||||
}
|
||||
|
||||
*timings_len = i + 1;
|
||||
}
|
||||
|
||||
// messages in input array for encoder should have one protocol
|
||||
static void infrared_test_run_encoder(InfraredProtocol protocol, uint32_t test_index) {
|
||||
uint32_t* timings;
|
||||
uint32_t timings_count = 200;
|
||||
uint32_t* expected_timings;
|
||||
uint32_t expected_timings_count;
|
||||
InfraredMessage* input_messages;
|
||||
uint32_t input_messages_count;
|
||||
|
||||
string_t buf;
|
||||
string_init(buf);
|
||||
|
||||
const char* protocol_name = infrared_get_protocol_name(protocol);
|
||||
mu_assert(infrared_test_prepare_file(protocol_name), "Failed to prepare test file");
|
||||
|
||||
string_printf(buf, "encoder_input%d", test_index);
|
||||
mu_assert(
|
||||
infrared_test_load_messages(
|
||||
test->ff, string_get_cstr(buf), &input_messages, &input_messages_count),
|
||||
"Failed to load messages from file");
|
||||
|
||||
string_printf(buf, "encoder_expected%d", test_index);
|
||||
mu_assert(
|
||||
infrared_test_load_raw_signal(
|
||||
test->ff, string_get_cstr(buf), &expected_timings, &expected_timings_count),
|
||||
"Failed to load raw signal from file");
|
||||
|
||||
flipper_format_buffered_file_close(test->ff);
|
||||
string_clear(buf);
|
||||
|
||||
uint32_t j = 0;
|
||||
timings = malloc(sizeof(uint32_t) * timings_count);
|
||||
|
||||
for(uint32_t message_counter = 0; message_counter < input_messages_count; ++message_counter) {
|
||||
const InfraredMessage* message = &input_messages[message_counter];
|
||||
if(!message->repeat) {
|
||||
infrared_reset_encoder(test->encoder_handler, message);
|
||||
}
|
||||
|
||||
timings_count = 200;
|
||||
infrared_test_run_encoder_fill_array(test->encoder_handler, timings, &timings_count, NULL);
|
||||
furi_check(timings_count <= 200);
|
||||
|
||||
for(size_t i = 0; i < timings_count; ++i, ++j) {
|
||||
mu_check(MATCH_TIMING(timings[i], expected_timings[j], 120));
|
||||
mu_assert(j < expected_timings_count, "encoded more timings than expected");
|
||||
}
|
||||
}
|
||||
|
||||
free(input_messages);
|
||||
free(expected_timings);
|
||||
free(timings);
|
||||
|
||||
mu_assert(j == expected_timings_count, "encoded less timings than expected");
|
||||
}
|
||||
|
||||
static void infrared_test_run_encoder_decoder(InfraredProtocol protocol, uint32_t test_index) {
|
||||
uint32_t* timings = 0;
|
||||
uint32_t timings_count = 200;
|
||||
InfraredMessage* input_messages;
|
||||
uint32_t input_messages_count;
|
||||
bool level = false;
|
||||
|
||||
string_t buf;
|
||||
string_init(buf);
|
||||
|
||||
timings = malloc(sizeof(uint32_t) * timings_count);
|
||||
|
||||
const char* protocol_name = infrared_get_protocol_name(protocol);
|
||||
mu_assert(infrared_test_prepare_file(protocol_name), "Failed to prepare test file");
|
||||
|
||||
string_printf(buf, "encoder_decoder_input%d", test_index);
|
||||
mu_assert(
|
||||
infrared_test_load_messages(
|
||||
test->ff, string_get_cstr(buf), &input_messages, &input_messages_count),
|
||||
"Failed to load messages from file");
|
||||
|
||||
flipper_format_buffered_file_close(test->ff);
|
||||
string_clear(buf);
|
||||
|
||||
for(uint32_t message_counter = 0; message_counter < input_messages_count; ++message_counter) {
|
||||
const InfraredMessage* message_encoded = &input_messages[message_counter];
|
||||
if(!message_encoded->repeat) {
|
||||
infrared_reset_encoder(test->encoder_handler, message_encoded);
|
||||
}
|
||||
|
||||
timings_count = 200;
|
||||
infrared_test_run_encoder_fill_array(
|
||||
test->encoder_handler, timings, &timings_count, &level);
|
||||
furi_check(timings_count <= 200);
|
||||
|
||||
const InfraredMessage* message_decoded = 0;
|
||||
for(size_t i = 0; i < timings_count; ++i) {
|
||||
message_decoded = infrared_decode(test->decoder_handler, level, timings[i]);
|
||||
if((i == timings_count - 2) && level && message_decoded) {
|
||||
/* In case we end with space timing - message can be decoded at last mark */
|
||||
break;
|
||||
} else if(i < timings_count - 1) {
|
||||
mu_check(!message_decoded);
|
||||
} else {
|
||||
if(!message_decoded) {
|
||||
message_decoded = infrared_check_decoder_ready(test->decoder_handler);
|
||||
}
|
||||
mu_check(message_decoded);
|
||||
}
|
||||
level = !level;
|
||||
}
|
||||
if(message_decoded) {
|
||||
infrared_test_compare_message_results(message_decoded, message_encoded);
|
||||
} else {
|
||||
mu_check(0);
|
||||
}
|
||||
}
|
||||
free(input_messages);
|
||||
free(timings);
|
||||
}
|
||||
|
||||
static void infrared_test_run_decoder(InfraredProtocol protocol, uint32_t test_index) {
|
||||
uint32_t* timings;
|
||||
uint32_t timings_count;
|
||||
InfraredMessage* messages;
|
||||
uint32_t messages_count;
|
||||
|
||||
string_t buf;
|
||||
string_init(buf);
|
||||
|
||||
mu_assert(
|
||||
infrared_test_prepare_file(infrared_get_protocol_name(protocol)),
|
||||
"Failed to prepare test file");
|
||||
|
||||
string_printf(buf, "decoder_input%d", test_index);
|
||||
mu_assert(
|
||||
infrared_test_load_raw_signal(test->ff, string_get_cstr(buf), &timings, &timings_count),
|
||||
"Failed to load raw signal from file");
|
||||
|
||||
string_printf(buf, "decoder_expected%d", test_index);
|
||||
mu_assert(
|
||||
infrared_test_load_messages(test->ff, string_get_cstr(buf), &messages, &messages_count),
|
||||
"Failed to load messages from file");
|
||||
|
||||
flipper_format_buffered_file_close(test->ff);
|
||||
string_clear(buf);
|
||||
|
||||
InfraredMessage message_decoded_check_local;
|
||||
bool level = 0;
|
||||
uint32_t message_counter = 0;
|
||||
const InfraredMessage* message_decoded = 0;
|
||||
|
||||
for(uint32_t i = 0; i < timings_count; ++i) {
|
||||
const InfraredMessage* message_decoded_check = 0;
|
||||
|
||||
if(timings[i] > INFRARED_RAW_RX_TIMING_DELAY_US) {
|
||||
message_decoded_check = infrared_check_decoder_ready(test->decoder_handler);
|
||||
if(message_decoded_check) {
|
||||
/* infrared_decode() can reset message, but we have to call infrared_decode() to perform real
|
||||
* simulation: infrared_check() by timeout, then infrared_decode() when meet edge */
|
||||
message_decoded_check_local = *message_decoded_check;
|
||||
message_decoded_check = &message_decoded_check_local;
|
||||
}
|
||||
}
|
||||
|
||||
message_decoded = infrared_decode(test->decoder_handler, level, timings[i]);
|
||||
|
||||
if(message_decoded_check || message_decoded) {
|
||||
mu_assert(
|
||||
!(message_decoded_check && message_decoded),
|
||||
"both messages decoded: check_ready() and infrared_decode()");
|
||||
|
||||
if(message_decoded_check) {
|
||||
message_decoded = message_decoded_check;
|
||||
}
|
||||
|
||||
mu_assert(message_counter < messages_count, "decoded more than expected");
|
||||
infrared_test_compare_message_results(message_decoded, &messages[message_counter]);
|
||||
|
||||
++message_counter;
|
||||
}
|
||||
level = !level;
|
||||
}
|
||||
|
||||
message_decoded = infrared_check_decoder_ready(test->decoder_handler);
|
||||
if(message_decoded) {
|
||||
infrared_test_compare_message_results(message_decoded, &messages[message_counter]);
|
||||
++message_counter;
|
||||
}
|
||||
|
||||
free(timings);
|
||||
free(messages);
|
||||
|
||||
mu_assert(message_counter == messages_count, "decoded less than expected");
|
||||
}
|
||||
|
||||
MU_TEST(infrared_test_decoder_samsung32) {
|
||||
infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
|
||||
}
|
||||
|
||||
MU_TEST(infrared_test_decoder_mixed) {
|
||||
infrared_test_run_decoder(InfraredProtocolRC5, 2);
|
||||
infrared_test_run_decoder(InfraredProtocolSIRC, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolNECext, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolRC6, 2);
|
||||
infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolRC6, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolRC5, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolSIRC, 2);
|
||||
infrared_test_run_decoder(InfraredProtocolNECext, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolSIRC, 4);
|
||||
infrared_test_run_decoder(InfraredProtocolNEC, 2);
|
||||
infrared_test_run_decoder(InfraredProtocolRC6, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolNECext, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolSIRC, 5);
|
||||
infrared_test_run_decoder(InfraredProtocolNEC, 3);
|
||||
infrared_test_run_decoder(InfraredProtocolRC5, 5);
|
||||
infrared_test_run_decoder(InfraredProtocolSamsung32, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolSIRC, 3);
|
||||
}
|
||||
|
||||
MU_TEST(infrared_test_decoder_nec) {
|
||||
infrared_test_run_decoder(InfraredProtocolNEC, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolNEC, 2);
|
||||
infrared_test_run_decoder(InfraredProtocolNEC, 3);
|
||||
}
|
||||
|
||||
MU_TEST(infrared_test_decoder_unexpected_end_in_sequence) {
|
||||
infrared_test_run_decoder(InfraredProtocolNEC, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolNEC, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolNEC, 2);
|
||||
infrared_test_run_decoder(InfraredProtocolNEC, 2);
|
||||
}
|
||||
|
||||
MU_TEST(infrared_test_decoder_necext1) {
|
||||
infrared_test_run_decoder(InfraredProtocolNECext, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolNECext, 1);
|
||||
}
|
||||
|
||||
MU_TEST(infrared_test_decoder_long_packets_with_nec_start) {
|
||||
infrared_test_run_decoder(InfraredProtocolNEC42ext, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolNEC42ext, 2);
|
||||
}
|
||||
|
||||
MU_TEST(infrared_test_encoder_sirc) {
|
||||
infrared_test_run_encoder(InfraredProtocolSIRC, 1);
|
||||
infrared_test_run_encoder(InfraredProtocolSIRC, 2);
|
||||
}
|
||||
|
||||
MU_TEST(infrared_test_decoder_sirc) {
|
||||
infrared_test_run_decoder(InfraredProtocolSIRC, 3);
|
||||
infrared_test_run_decoder(InfraredProtocolSIRC, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolSIRC, 2);
|
||||
infrared_test_run_decoder(InfraredProtocolSIRC, 4);
|
||||
infrared_test_run_decoder(InfraredProtocolSIRC, 5);
|
||||
}
|
||||
|
||||
MU_TEST(infrared_test_decoder_rc5) {
|
||||
infrared_test_run_decoder(InfraredProtocolRC5X, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolRC5, 1);
|
||||
infrared_test_run_decoder(InfraredProtocolRC5, 2);
|
||||
infrared_test_run_decoder(InfraredProtocolRC5, 3);
|
||||
infrared_test_run_decoder(InfraredProtocolRC5, 4);
|
||||
infrared_test_run_decoder(InfraredProtocolRC5, 5);
|
||||
infrared_test_run_decoder(InfraredProtocolRC5, 6);
|
||||
infrared_test_run_decoder(InfraredProtocolRC5, 7);
|
||||
}
|
||||
|
||||
MU_TEST(infrared_test_encoder_rc5x) {
|
||||
infrared_test_run_encoder(InfraredProtocolRC5X, 1);
|
||||
}
|
||||
|
||||
MU_TEST(infrared_test_encoder_rc5) {
|
||||
infrared_test_run_encoder(InfraredProtocolRC5, 1);
|
||||
}
|
||||
|
||||
MU_TEST(infrared_test_decoder_rc6) {
|
||||
infrared_test_run_decoder(InfraredProtocolRC6, 1);
|
||||
}
|
||||
|
||||
MU_TEST(infrared_test_encoder_rc6) {
|
||||
infrared_test_run_encoder(InfraredProtocolRC6, 1);
|
||||
}
|
||||
|
||||
MU_TEST(infrared_test_encoder_decoder_all) {
|
||||
infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1);
|
||||
infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1);
|
||||
infrared_test_run_encoder_decoder(InfraredProtocolNEC42, 1);
|
||||
infrared_test_run_encoder_decoder(InfraredProtocolNEC42ext, 1);
|
||||
infrared_test_run_encoder_decoder(InfraredProtocolSamsung32, 1);
|
||||
infrared_test_run_encoder_decoder(InfraredProtocolRC6, 1);
|
||||
infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1);
|
||||
infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(infrared_test) {
|
||||
MU_SUITE_CONFIGURE(&infrared_test_alloc, &infrared_test_free);
|
||||
|
||||
MU_RUN_TEST(infrared_test_encoder_sirc);
|
||||
MU_RUN_TEST(infrared_test_decoder_sirc);
|
||||
MU_RUN_TEST(infrared_test_encoder_rc5x);
|
||||
MU_RUN_TEST(infrared_test_encoder_rc5);
|
||||
MU_RUN_TEST(infrared_test_decoder_rc5);
|
||||
MU_RUN_TEST(infrared_test_decoder_rc6);
|
||||
MU_RUN_TEST(infrared_test_encoder_rc6);
|
||||
MU_RUN_TEST(infrared_test_decoder_unexpected_end_in_sequence);
|
||||
MU_RUN_TEST(infrared_test_decoder_long_packets_with_nec_start);
|
||||
MU_RUN_TEST(infrared_test_decoder_nec);
|
||||
MU_RUN_TEST(infrared_test_decoder_samsung32);
|
||||
MU_RUN_TEST(infrared_test_decoder_necext1);
|
||||
MU_RUN_TEST(infrared_test_decoder_mixed);
|
||||
MU_RUN_TEST(infrared_test_encoder_decoder_all);
|
||||
}
|
||||
|
||||
int run_minunit_test_infrared() {
|
||||
MU_RUN_SUITE(infrared_test);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
473
applications/debug/unit_tests/lfrfid/bit_lib_test.c
Normal file
473
applications/debug/unit_tests/lfrfid/bit_lib_test.c
Normal file
@@ -0,0 +1,473 @@
|
||||
#include <furi.h>
|
||||
#include "../minunit.h"
|
||||
#include <lfrfid/tools/bit_lib.h>
|
||||
|
||||
MU_TEST(test_bit_lib_increment_index) {
|
||||
uint32_t index = 0;
|
||||
|
||||
// test increment
|
||||
for(uint32_t i = 0; i < 31; ++i) {
|
||||
bit_lib_increment_index(index, 32);
|
||||
mu_assert_int_eq(i + 1, index);
|
||||
}
|
||||
|
||||
// test wrap around
|
||||
for(uint32_t i = 0; i < 512; ++i) {
|
||||
bit_lib_increment_index(index, 32);
|
||||
mu_assert_int_less_than(32, index);
|
||||
}
|
||||
}
|
||||
|
||||
MU_TEST(test_bit_lib_is_set) {
|
||||
uint32_t value = 0x0000FFFF;
|
||||
|
||||
for(uint32_t i = 0; i < 16; ++i) {
|
||||
mu_check(bit_lib_bit_is_set(value, i));
|
||||
mu_check(!bit_lib_bit_is_not_set(value, i));
|
||||
}
|
||||
|
||||
for(uint32_t i = 16; i < 32; ++i) {
|
||||
mu_check(!bit_lib_bit_is_set(value, i));
|
||||
mu_check(bit_lib_bit_is_not_set(value, i));
|
||||
}
|
||||
}
|
||||
|
||||
MU_TEST(test_bit_lib_push) {
|
||||
#define TEST_BIT_LIB_PUSH_DATA_SIZE 4
|
||||
uint8_t data[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0};
|
||||
uint8_t expected_data_1[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0x00, 0x0F, 0xFF};
|
||||
uint8_t expected_data_2[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0xFF, 0xF0, 0x00};
|
||||
uint8_t expected_data_3[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xFF, 0x00, 0x00, 0xFF};
|
||||
uint8_t expected_data_4[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF};
|
||||
uint8_t expected_data_5[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0x00, 0x00, 0x00};
|
||||
uint8_t expected_data_6[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xCC, 0xCC, 0xCC, 0xCC};
|
||||
|
||||
for(uint32_t i = 0; i < 12; ++i) {
|
||||
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);
|
||||
}
|
||||
mu_assert_mem_eq(expected_data_1, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
|
||||
|
||||
for(uint32_t i = 0; i < 12; ++i) {
|
||||
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);
|
||||
}
|
||||
mu_assert_mem_eq(expected_data_2, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
|
||||
|
||||
for(uint32_t i = 0; i < 4; ++i) {
|
||||
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);
|
||||
}
|
||||
for(uint32_t i = 0; i < 8; ++i) {
|
||||
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);
|
||||
}
|
||||
mu_assert_mem_eq(expected_data_3, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
|
||||
|
||||
for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 8; ++i) {
|
||||
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);
|
||||
}
|
||||
mu_assert_mem_eq(expected_data_4, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
|
||||
|
||||
for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 8; ++i) {
|
||||
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);
|
||||
}
|
||||
mu_assert_mem_eq(expected_data_5, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
|
||||
|
||||
for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 2; ++i) {
|
||||
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);
|
||||
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true);
|
||||
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);
|
||||
bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false);
|
||||
}
|
||||
mu_assert_mem_eq(expected_data_6, data, TEST_BIT_LIB_PUSH_DATA_SIZE);
|
||||
}
|
||||
|
||||
MU_TEST(test_bit_lib_set_bit) {
|
||||
uint8_t value[2] = {0x00, 0xFF};
|
||||
bit_lib_set_bit(value, 15, false);
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xFE}), 2);
|
||||
bit_lib_set_bit(value, 14, false);
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xFC}), 2);
|
||||
bit_lib_set_bit(value, 13, false);
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xF8}), 2);
|
||||
bit_lib_set_bit(value, 12, false);
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xF0}), 2);
|
||||
bit_lib_set_bit(value, 11, false);
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xE0}), 2);
|
||||
bit_lib_set_bit(value, 10, false);
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xC0}), 2);
|
||||
bit_lib_set_bit(value, 9, false);
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0x80}), 2);
|
||||
bit_lib_set_bit(value, 8, false);
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0x00}), 2);
|
||||
|
||||
bit_lib_set_bit(value, 7, true);
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0x01, 0x00}), 2);
|
||||
bit_lib_set_bit(value, 6, true);
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0x03, 0x00}), 2);
|
||||
bit_lib_set_bit(value, 5, true);
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0x07, 0x00}), 2);
|
||||
bit_lib_set_bit(value, 4, true);
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0x0F, 0x00}), 2);
|
||||
bit_lib_set_bit(value, 3, true);
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0x1F, 0x00}), 2);
|
||||
bit_lib_set_bit(value, 2, true);
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0x3F, 0x00}), 2);
|
||||
bit_lib_set_bit(value, 1, true);
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0x7F, 0x00}), 2);
|
||||
bit_lib_set_bit(value, 0, true);
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0xFF, 0x00}), 2);
|
||||
}
|
||||
|
||||
MU_TEST(test_bit_lib_set_bits) {
|
||||
uint8_t value[2] = {0b00000000, 0b11111111};
|
||||
// set 4 bits to 0b0100 from 12 index
|
||||
bit_lib_set_bits(value, 12, 0b0100, 4);
|
||||
// [0100]
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0b00000000, 0b11110100}), 2);
|
||||
|
||||
// set 2 bits to 0b11 from 11 index
|
||||
bit_lib_set_bits(value, 11, 0b11, 2);
|
||||
// [11]
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0b00000000, 0b11111100}), 2);
|
||||
|
||||
// set 3 bits to 0b111 from 0 index
|
||||
bit_lib_set_bits(value, 0, 0b111, 3);
|
||||
// [111]
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0b11100000, 0b11111100}), 2);
|
||||
|
||||
// set 8 bits to 0b11111000 from 3 index
|
||||
bit_lib_set_bits(value, 3, 0b11111000, 8);
|
||||
// [11111 000]
|
||||
mu_assert_mem_eq(value, ((uint8_t[]){0b11111111, 0b00011100}), 2);
|
||||
}
|
||||
|
||||
MU_TEST(test_bit_lib_get_bit) {
|
||||
uint8_t value[2] = {0b00000000, 0b11111111};
|
||||
for(uint32_t i = 0; i < 8; ++i) {
|
||||
mu_check(bit_lib_get_bit(value, i) == false);
|
||||
}
|
||||
for(uint32_t i = 8; i < 16; ++i) {
|
||||
mu_check(bit_lib_get_bit(value, i) == true);
|
||||
}
|
||||
}
|
||||
|
||||
MU_TEST(test_bit_lib_get_bits) {
|
||||
uint8_t value[2] = {0b00000000, 0b11111111};
|
||||
mu_assert_int_eq(0b00000000, bit_lib_get_bits(value, 0, 8));
|
||||
mu_assert_int_eq(0b00000001, bit_lib_get_bits(value, 1, 8));
|
||||
mu_assert_int_eq(0b00000011, bit_lib_get_bits(value, 2, 8));
|
||||
mu_assert_int_eq(0b00000111, bit_lib_get_bits(value, 3, 8));
|
||||
mu_assert_int_eq(0b00001111, bit_lib_get_bits(value, 4, 8));
|
||||
mu_assert_int_eq(0b00011111, bit_lib_get_bits(value, 5, 8));
|
||||
mu_assert_int_eq(0b00111111, bit_lib_get_bits(value, 6, 8));
|
||||
mu_assert_int_eq(0b01111111, bit_lib_get_bits(value, 7, 8));
|
||||
mu_assert_int_eq(0b11111111, bit_lib_get_bits(value, 8, 8));
|
||||
}
|
||||
|
||||
MU_TEST(test_bit_lib_get_bits_16) {
|
||||
uint8_t value[2] = {0b00001001, 0b10110001};
|
||||
mu_assert_int_eq(0b0, bit_lib_get_bits_16(value, 0, 1));
|
||||
mu_assert_int_eq(0b00, bit_lib_get_bits_16(value, 0, 2));
|
||||
mu_assert_int_eq(0b000, bit_lib_get_bits_16(value, 0, 3));
|
||||
mu_assert_int_eq(0b0000, bit_lib_get_bits_16(value, 0, 4));
|
||||
mu_assert_int_eq(0b00001, bit_lib_get_bits_16(value, 0, 5));
|
||||
mu_assert_int_eq(0b000010, bit_lib_get_bits_16(value, 0, 6));
|
||||
mu_assert_int_eq(0b0000100, bit_lib_get_bits_16(value, 0, 7));
|
||||
mu_assert_int_eq(0b00001001, bit_lib_get_bits_16(value, 0, 8));
|
||||
mu_assert_int_eq(0b000010011, bit_lib_get_bits_16(value, 0, 9));
|
||||
mu_assert_int_eq(0b0000100110, bit_lib_get_bits_16(value, 0, 10));
|
||||
mu_assert_int_eq(0b00001001101, bit_lib_get_bits_16(value, 0, 11));
|
||||
mu_assert_int_eq(0b000010011011, bit_lib_get_bits_16(value, 0, 12));
|
||||
mu_assert_int_eq(0b0000100110110, bit_lib_get_bits_16(value, 0, 13));
|
||||
mu_assert_int_eq(0b00001001101100, bit_lib_get_bits_16(value, 0, 14));
|
||||
mu_assert_int_eq(0b000010011011000, bit_lib_get_bits_16(value, 0, 15));
|
||||
mu_assert_int_eq(0b0000100110110001, bit_lib_get_bits_16(value, 0, 16));
|
||||
}
|
||||
|
||||
MU_TEST(test_bit_lib_get_bits_32) {
|
||||
uint8_t value[4] = {0b00001001, 0b10110001, 0b10001100, 0b01100010};
|
||||
mu_assert_int_eq(0b0, bit_lib_get_bits_32(value, 0, 1));
|
||||
mu_assert_int_eq(0b00, bit_lib_get_bits_32(value, 0, 2));
|
||||
mu_assert_int_eq(0b000, bit_lib_get_bits_32(value, 0, 3));
|
||||
mu_assert_int_eq(0b0000, bit_lib_get_bits_32(value, 0, 4));
|
||||
mu_assert_int_eq(0b00001, bit_lib_get_bits_32(value, 0, 5));
|
||||
mu_assert_int_eq(0b000010, bit_lib_get_bits_32(value, 0, 6));
|
||||
mu_assert_int_eq(0b0000100, bit_lib_get_bits_32(value, 0, 7));
|
||||
mu_assert_int_eq(0b00001001, bit_lib_get_bits_32(value, 0, 8));
|
||||
mu_assert_int_eq(0b000010011, bit_lib_get_bits_32(value, 0, 9));
|
||||
mu_assert_int_eq(0b0000100110, bit_lib_get_bits_32(value, 0, 10));
|
||||
mu_assert_int_eq(0b00001001101, bit_lib_get_bits_32(value, 0, 11));
|
||||
mu_assert_int_eq(0b000010011011, bit_lib_get_bits_32(value, 0, 12));
|
||||
mu_assert_int_eq(0b0000100110110, bit_lib_get_bits_32(value, 0, 13));
|
||||
mu_assert_int_eq(0b00001001101100, bit_lib_get_bits_32(value, 0, 14));
|
||||
mu_assert_int_eq(0b000010011011000, bit_lib_get_bits_32(value, 0, 15));
|
||||
mu_assert_int_eq(0b0000100110110001, bit_lib_get_bits_32(value, 0, 16));
|
||||
mu_assert_int_eq(0b00001001101100011, bit_lib_get_bits_32(value, 0, 17));
|
||||
mu_assert_int_eq(0b000010011011000110, bit_lib_get_bits_32(value, 0, 18));
|
||||
mu_assert_int_eq(0b0000100110110001100, bit_lib_get_bits_32(value, 0, 19));
|
||||
mu_assert_int_eq(0b00001001101100011000, bit_lib_get_bits_32(value, 0, 20));
|
||||
mu_assert_int_eq(0b000010011011000110001, bit_lib_get_bits_32(value, 0, 21));
|
||||
mu_assert_int_eq(0b0000100110110001100011, bit_lib_get_bits_32(value, 0, 22));
|
||||
mu_assert_int_eq(0b00001001101100011000110, bit_lib_get_bits_32(value, 0, 23));
|
||||
mu_assert_int_eq(0b000010011011000110001100, bit_lib_get_bits_32(value, 0, 24));
|
||||
mu_assert_int_eq(0b0000100110110001100011000, bit_lib_get_bits_32(value, 0, 25));
|
||||
mu_assert_int_eq(0b00001001101100011000110001, bit_lib_get_bits_32(value, 0, 26));
|
||||
mu_assert_int_eq(0b000010011011000110001100011, bit_lib_get_bits_32(value, 0, 27));
|
||||
mu_assert_int_eq(0b0000100110110001100011000110, bit_lib_get_bits_32(value, 0, 28));
|
||||
mu_assert_int_eq(0b00001001101100011000110001100, bit_lib_get_bits_32(value, 0, 29));
|
||||
mu_assert_int_eq(0b000010011011000110001100011000, bit_lib_get_bits_32(value, 0, 30));
|
||||
mu_assert_int_eq(0b0000100110110001100011000110001, bit_lib_get_bits_32(value, 0, 31));
|
||||
mu_assert_int_eq(0b00001001101100011000110001100010, bit_lib_get_bits_32(value, 0, 32));
|
||||
}
|
||||
|
||||
MU_TEST(test_bit_lib_test_parity_u32) {
|
||||
// test even parity
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00000000, BitLibParityEven), 0);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00000001, BitLibParityEven), 1);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00000010, BitLibParityEven), 1);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00000011, BitLibParityEven), 0);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00000100, BitLibParityEven), 1);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00000101, BitLibParityEven), 0);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00000110, BitLibParityEven), 0);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00000111, BitLibParityEven), 1);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00001000, BitLibParityEven), 1);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00001001, BitLibParityEven), 0);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00001010, BitLibParityEven), 0);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00001011, BitLibParityEven), 1);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00001100, BitLibParityEven), 0);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00001101, BitLibParityEven), 1);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00001110, BitLibParityEven), 1);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00001111, BitLibParityEven), 0);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00010000, BitLibParityEven), 1);
|
||||
|
||||
// test odd parity
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00000000, BitLibParityOdd), 1);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00000001, BitLibParityOdd), 0);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00000010, BitLibParityOdd), 0);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00000011, BitLibParityOdd), 1);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00000100, BitLibParityOdd), 0);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00000101, BitLibParityOdd), 1);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00000110, BitLibParityOdd), 1);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00000111, BitLibParityOdd), 0);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00001000, BitLibParityOdd), 0);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00001001, BitLibParityOdd), 1);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00001010, BitLibParityOdd), 1);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00001011, BitLibParityOdd), 0);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00001100, BitLibParityOdd), 1);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00001101, BitLibParityOdd), 0);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00001110, BitLibParityOdd), 0);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00001111, BitLibParityOdd), 1);
|
||||
mu_assert_int_eq(bit_lib_test_parity_32(0b00010000, BitLibParityOdd), 0);
|
||||
}
|
||||
|
||||
MU_TEST(test_bit_lib_test_parity) {
|
||||
// next data contains valid parity for 1-3 nibble and invalid for 4 nibble
|
||||
uint8_t data_always_0_parity[2] = {0b11101110, 0b11101111};
|
||||
uint8_t data_always_1_parity[2] = {0b00010001, 0b00010000};
|
||||
uint8_t data_always_odd_parity[2] = {0b00000011, 0b11110111};
|
||||
uint8_t data_always_even_parity[2] = {0b00010111, 0b10110011};
|
||||
|
||||
// test alawys 0 parity
|
||||
mu_check(bit_lib_test_parity(data_always_0_parity, 0, 12, BitLibParityAlways0, 4));
|
||||
mu_check(bit_lib_test_parity(data_always_0_parity, 4, 8, BitLibParityAlways0, 4));
|
||||
mu_check(bit_lib_test_parity(data_always_0_parity, 8, 4, BitLibParityAlways0, 4));
|
||||
mu_check(bit_lib_test_parity(data_always_1_parity, 12, 4, BitLibParityAlways0, 4));
|
||||
|
||||
mu_check(!bit_lib_test_parity(data_always_0_parity, 0, 16, BitLibParityAlways0, 4));
|
||||
mu_check(!bit_lib_test_parity(data_always_0_parity, 4, 12, BitLibParityAlways0, 4));
|
||||
mu_check(!bit_lib_test_parity(data_always_0_parity, 8, 8, BitLibParityAlways0, 4));
|
||||
mu_check(!bit_lib_test_parity(data_always_0_parity, 12, 4, BitLibParityAlways0, 4));
|
||||
|
||||
// test alawys 1 parity
|
||||
mu_check(bit_lib_test_parity(data_always_1_parity, 0, 12, BitLibParityAlways1, 4));
|
||||
mu_check(bit_lib_test_parity(data_always_1_parity, 4, 8, BitLibParityAlways1, 4));
|
||||
mu_check(bit_lib_test_parity(data_always_1_parity, 8, 4, BitLibParityAlways1, 4));
|
||||
mu_check(bit_lib_test_parity(data_always_0_parity, 12, 4, BitLibParityAlways1, 4));
|
||||
|
||||
mu_check(!bit_lib_test_parity(data_always_1_parity, 0, 16, BitLibParityAlways1, 4));
|
||||
mu_check(!bit_lib_test_parity(data_always_1_parity, 4, 12, BitLibParityAlways1, 4));
|
||||
mu_check(!bit_lib_test_parity(data_always_1_parity, 8, 8, BitLibParityAlways1, 4));
|
||||
mu_check(!bit_lib_test_parity(data_always_1_parity, 12, 4, BitLibParityAlways1, 4));
|
||||
|
||||
// test odd parity
|
||||
mu_check(bit_lib_test_parity(data_always_odd_parity, 0, 12, BitLibParityOdd, 4));
|
||||
mu_check(bit_lib_test_parity(data_always_odd_parity, 4, 8, BitLibParityOdd, 4));
|
||||
mu_check(bit_lib_test_parity(data_always_odd_parity, 8, 4, BitLibParityOdd, 4));
|
||||
mu_check(bit_lib_test_parity(data_always_even_parity, 12, 4, BitLibParityOdd, 4));
|
||||
|
||||
mu_check(!bit_lib_test_parity(data_always_odd_parity, 0, 16, BitLibParityOdd, 4));
|
||||
mu_check(!bit_lib_test_parity(data_always_odd_parity, 4, 12, BitLibParityOdd, 4));
|
||||
mu_check(!bit_lib_test_parity(data_always_odd_parity, 8, 8, BitLibParityOdd, 4));
|
||||
mu_check(!bit_lib_test_parity(data_always_odd_parity, 12, 4, BitLibParityOdd, 4));
|
||||
|
||||
// test even parity
|
||||
mu_check(bit_lib_test_parity(data_always_even_parity, 0, 12, BitLibParityEven, 4));
|
||||
mu_check(bit_lib_test_parity(data_always_even_parity, 4, 8, BitLibParityEven, 4));
|
||||
mu_check(bit_lib_test_parity(data_always_even_parity, 8, 4, BitLibParityEven, 4));
|
||||
mu_check(bit_lib_test_parity(data_always_odd_parity, 12, 4, BitLibParityEven, 4));
|
||||
|
||||
mu_check(!bit_lib_test_parity(data_always_even_parity, 0, 16, BitLibParityEven, 4));
|
||||
mu_check(!bit_lib_test_parity(data_always_even_parity, 4, 12, BitLibParityEven, 4));
|
||||
mu_check(!bit_lib_test_parity(data_always_even_parity, 8, 8, BitLibParityEven, 4));
|
||||
mu_check(!bit_lib_test_parity(data_always_even_parity, 12, 4, BitLibParityEven, 4));
|
||||
}
|
||||
|
||||
MU_TEST(test_bit_lib_remove_bit_every_nth) {
|
||||
// TODO: more tests
|
||||
uint8_t data_i[1] = {0b00001111};
|
||||
uint8_t data_o[1] = {0b00011111};
|
||||
size_t length;
|
||||
|
||||
length = bit_lib_remove_bit_every_nth(data_i, 0, 8, 3);
|
||||
mu_assert_int_eq(6, length);
|
||||
mu_assert_mem_eq(data_o, data_i, 1);
|
||||
}
|
||||
|
||||
MU_TEST(test_bit_lib_reverse_bits) {
|
||||
uint8_t data_1_i[2] = {0b11001010, 0b00011111};
|
||||
uint8_t data_1_o[2] = {0b11111000, 0b01010011};
|
||||
|
||||
// reverse bits [0..15]
|
||||
bit_lib_reverse_bits(data_1_i, 0, 16);
|
||||
mu_assert_mem_eq(data_1_o, data_1_i, 2);
|
||||
|
||||
uint8_t data_2_i[2] = {0b11001010, 0b00011111};
|
||||
uint8_t data_2_o[2] = {0b11001000, 0b01011111};
|
||||
|
||||
// reverse bits [4..11]
|
||||
bit_lib_reverse_bits(data_2_i, 4, 8);
|
||||
mu_assert_mem_eq(data_2_o, data_2_i, 2);
|
||||
}
|
||||
|
||||
MU_TEST(test_bit_lib_copy_bits) {
|
||||
uint8_t data_1_i[2] = {0b11001010, 0b00011111};
|
||||
uint8_t data_1_o[2] = {0};
|
||||
|
||||
// data_1_o[0..15] = data_1_i[0..15]
|
||||
bit_lib_copy_bits(data_1_o, 0, 16, data_1_i, 0);
|
||||
mu_assert_mem_eq(data_1_i, data_1_o, 2);
|
||||
|
||||
memset(data_1_o, 0, 2);
|
||||
// data_1_o[4..11] = data_1_i[0..7]
|
||||
bit_lib_copy_bits(data_1_o, 4, 8, data_1_i, 0);
|
||||
mu_assert_mem_eq(((uint8_t[]){0b00001100, 0b10100000}), data_1_o, 2);
|
||||
}
|
||||
|
||||
MU_TEST(test_bit_lib_get_bit_count) {
|
||||
mu_assert_int_eq(0, bit_lib_get_bit_count(0));
|
||||
mu_assert_int_eq(1, bit_lib_get_bit_count(0b1));
|
||||
mu_assert_int_eq(1, bit_lib_get_bit_count(0b10));
|
||||
mu_assert_int_eq(2, bit_lib_get_bit_count(0b11));
|
||||
mu_assert_int_eq(4, bit_lib_get_bit_count(0b11000011));
|
||||
mu_assert_int_eq(6, bit_lib_get_bit_count(0b11000011000011));
|
||||
mu_assert_int_eq(8, bit_lib_get_bit_count(0b11111111));
|
||||
mu_assert_int_eq(16, bit_lib_get_bit_count(0b11111110000000000000000111111111));
|
||||
mu_assert_int_eq(32, bit_lib_get_bit_count(0b11111111111111111111111111111111));
|
||||
}
|
||||
|
||||
MU_TEST(test_bit_lib_reverse_16_fast) {
|
||||
mu_assert_int_eq(0b0000000000000000, bit_lib_reverse_16_fast(0b0000000000000000));
|
||||
mu_assert_int_eq(0b1000000000000000, bit_lib_reverse_16_fast(0b0000000000000001));
|
||||
mu_assert_int_eq(0b1100000000000000, bit_lib_reverse_16_fast(0b0000000000000011));
|
||||
mu_assert_int_eq(0b0000100000001001, bit_lib_reverse_16_fast(0b1001000000010000));
|
||||
}
|
||||
|
||||
MU_TEST(test_bit_lib_crc16) {
|
||||
uint8_t data[9] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'};
|
||||
uint8_t data_size = 9;
|
||||
|
||||
// Algorithm
|
||||
// Check Poly Init RefIn RefOut XorOut
|
||||
// CRC-16/CCITT-FALSE
|
||||
// 0x29B1 0x1021 0xFFFF false false 0x0000
|
||||
mu_assert_int_eq(0x29B1, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, false, false, 0x0000));
|
||||
// CRC-16/ARC
|
||||
// 0xBB3D 0x8005 0x0000 true true 0x0000
|
||||
mu_assert_int_eq(0xBB3D, bit_lib_crc16(data, data_size, 0x8005, 0x0000, true, true, 0x0000));
|
||||
// CRC-16/AUG-CCITT
|
||||
// 0xE5CC 0x1021 0x1D0F false false 0x0000
|
||||
mu_assert_int_eq(0xE5CC, bit_lib_crc16(data, data_size, 0x1021, 0x1D0F, false, false, 0x0000));
|
||||
// CRC-16/BUYPASS
|
||||
// 0xFEE8 0x8005 0x0000 false false 0x0000
|
||||
mu_assert_int_eq(0xFEE8, bit_lib_crc16(data, data_size, 0x8005, 0x0000, false, false, 0x0000));
|
||||
// CRC-16/CDMA2000
|
||||
// 0x4C06 0xC867 0xFFFF false false 0x0000
|
||||
mu_assert_int_eq(0x4C06, bit_lib_crc16(data, data_size, 0xC867, 0xFFFF, false, false, 0x0000));
|
||||
// CRC-16/DDS-110
|
||||
// 0x9ECF 0x8005 0x800D false false 0x0000
|
||||
mu_assert_int_eq(0x9ECF, bit_lib_crc16(data, data_size, 0x8005, 0x800D, false, false, 0x0000));
|
||||
// CRC-16/DECT-R
|
||||
// 0x007E 0x0589 0x0000 false false 0x0001
|
||||
mu_assert_int_eq(0x007E, bit_lib_crc16(data, data_size, 0x0589, 0x0000, false, false, 0x0001));
|
||||
// CRC-16/DECT-X
|
||||
// 0x007F 0x0589 0x0000 false false 0x0000
|
||||
mu_assert_int_eq(0x007F, bit_lib_crc16(data, data_size, 0x0589, 0x0000, false, false, 0x0000));
|
||||
// CRC-16/DNP
|
||||
// 0xEA82 0x3D65 0x0000 true true 0xFFFF
|
||||
mu_assert_int_eq(0xEA82, bit_lib_crc16(data, data_size, 0x3D65, 0x0000, true, true, 0xFFFF));
|
||||
// CRC-16/EN-13757
|
||||
// 0xC2B7 0x3D65 0x0000 false false 0xFFFF
|
||||
mu_assert_int_eq(0xC2B7, bit_lib_crc16(data, data_size, 0x3D65, 0x0000, false, false, 0xFFFF));
|
||||
// CRC-16/GENIBUS
|
||||
// 0xD64E 0x1021 0xFFFF false false 0xFFFF
|
||||
mu_assert_int_eq(0xD64E, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, false, false, 0xFFFF));
|
||||
// CRC-16/MAXIM
|
||||
// 0x44C2 0x8005 0x0000 true true 0xFFFF
|
||||
mu_assert_int_eq(0x44C2, bit_lib_crc16(data, data_size, 0x8005, 0x0000, true, true, 0xFFFF));
|
||||
// CRC-16/MCRF4XX
|
||||
// 0x6F91 0x1021 0xFFFF true true 0x0000
|
||||
mu_assert_int_eq(0x6F91, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, true, true, 0x0000));
|
||||
// CRC-16/RIELLO
|
||||
// 0x63D0 0x1021 0xB2AA true true 0x0000
|
||||
mu_assert_int_eq(0x63D0, bit_lib_crc16(data, data_size, 0x1021, 0xB2AA, true, true, 0x0000));
|
||||
// CRC-16/T10-DIF
|
||||
// 0xD0DB 0x8BB7 0x0000 false false 0x0000
|
||||
mu_assert_int_eq(0xD0DB, bit_lib_crc16(data, data_size, 0x8BB7, 0x0000, false, false, 0x0000));
|
||||
// CRC-16/TELEDISK
|
||||
// 0x0FB3 0xA097 0x0000 false false 0x0000
|
||||
mu_assert_int_eq(0x0FB3, bit_lib_crc16(data, data_size, 0xA097, 0x0000, false, false, 0x0000));
|
||||
// CRC-16/TMS37157
|
||||
// 0x26B1 0x1021 0x89EC true true 0x0000
|
||||
mu_assert_int_eq(0x26B1, bit_lib_crc16(data, data_size, 0x1021, 0x89EC, true, true, 0x0000));
|
||||
// CRC-16/USB
|
||||
// 0xB4C8 0x8005 0xFFFF true true 0xFFFF
|
||||
mu_assert_int_eq(0xB4C8, bit_lib_crc16(data, data_size, 0x8005, 0xFFFF, true, true, 0xFFFF));
|
||||
// CRC-A
|
||||
// 0xBF05 0x1021 0xC6C6 true true 0x0000
|
||||
mu_assert_int_eq(0xBF05, bit_lib_crc16(data, data_size, 0x1021, 0xC6C6, true, true, 0x0000));
|
||||
// CRC-16/KERMIT
|
||||
// 0x2189 0x1021 0x0000 true true 0x0000
|
||||
mu_assert_int_eq(0x2189, bit_lib_crc16(data, data_size, 0x1021, 0x0000, true, true, 0x0000));
|
||||
// CRC-16/MODBUS
|
||||
// 0x4B37 0x8005 0xFFFF true true 0x0000
|
||||
mu_assert_int_eq(0x4B37, bit_lib_crc16(data, data_size, 0x8005, 0xFFFF, true, true, 0x0000));
|
||||
// CRC-16/X-25
|
||||
// 0x906E 0x1021 0xFFFF true true 0xFFFF
|
||||
mu_assert_int_eq(0x906E, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, true, true, 0xFFFF));
|
||||
// CRC-16/XMODEM
|
||||
// 0x31C3 0x1021 0x0000 false false 0x0000
|
||||
mu_assert_int_eq(0x31C3, bit_lib_crc16(data, data_size, 0x1021, 0x0000, false, false, 0x0000));
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(test_bit_lib) {
|
||||
MU_RUN_TEST(test_bit_lib_increment_index);
|
||||
MU_RUN_TEST(test_bit_lib_is_set);
|
||||
MU_RUN_TEST(test_bit_lib_push);
|
||||
MU_RUN_TEST(test_bit_lib_set_bit);
|
||||
MU_RUN_TEST(test_bit_lib_set_bits);
|
||||
MU_RUN_TEST(test_bit_lib_get_bit);
|
||||
MU_RUN_TEST(test_bit_lib_get_bits);
|
||||
MU_RUN_TEST(test_bit_lib_get_bits_16);
|
||||
MU_RUN_TEST(test_bit_lib_get_bits_32);
|
||||
MU_RUN_TEST(test_bit_lib_test_parity_u32);
|
||||
MU_RUN_TEST(test_bit_lib_test_parity);
|
||||
MU_RUN_TEST(test_bit_lib_remove_bit_every_nth);
|
||||
MU_RUN_TEST(test_bit_lib_copy_bits);
|
||||
MU_RUN_TEST(test_bit_lib_reverse_bits);
|
||||
MU_RUN_TEST(test_bit_lib_get_bit_count);
|
||||
MU_RUN_TEST(test_bit_lib_reverse_16_fast);
|
||||
MU_RUN_TEST(test_bit_lib_crc16);
|
||||
}
|
||||
|
||||
int run_minunit_test_bit_lib() {
|
||||
MU_RUN_SUITE(test_bit_lib);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
464
applications/debug/unit_tests/lfrfid/lfrfid_protocols.c
Normal file
464
applications/debug/unit_tests/lfrfid/lfrfid_protocols.c
Normal file
@@ -0,0 +1,464 @@
|
||||
#include <furi.h>
|
||||
#include "../minunit.h"
|
||||
#include <toolbox/protocols/protocol_dict.h>
|
||||
#include <lfrfid/protocols/lfrfid_protocols.h>
|
||||
#include <toolbox/pulse_protocols/pulse_glue.h>
|
||||
|
||||
#define LF_RFID_READ_TIMING_MULTIPLIER 8
|
||||
|
||||
#define EM_TEST_DATA \
|
||||
{ 0x58, 0x00, 0x85, 0x64, 0x02 }
|
||||
#define EM_TEST_DATA_SIZE 5
|
||||
#define EM_TEST_EMULATION_TIMINGS_COUNT (64 * 2)
|
||||
|
||||
const int8_t em_test_timings[EM_TEST_EMULATION_TIMINGS_COUNT] = {
|
||||
32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, -32,
|
||||
32, 32, -32, -32, 32, 32, -32, -32, 32, 32, -32, -32, 32, -32, 32, -32, 32, 32, -32,
|
||||
-32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32,
|
||||
32, 32, -32, -32, 32, -32, 32, -32, 32, 32, -32, -32, 32, 32, -32, -32, 32, 32, -32,
|
||||
-32, 32, -32, 32, 32, -32, 32, -32, -32, 32, -32, 32, -32, 32, 32, -32, -32, 32, -32,
|
||||
32, 32, -32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, 32, -32,
|
||||
-32, 32, 32, -32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32,
|
||||
};
|
||||
|
||||
#define HID10301_TEST_DATA \
|
||||
{ 0x8D, 0x48, 0xA8 }
|
||||
#define HID10301_TEST_DATA_SIZE 3
|
||||
#define HID10301_TEST_EMULATION_TIMINGS_COUNT (541 * 2)
|
||||
|
||||
const int8_t hid10301_test_timings[HID10301_TEST_EMULATION_TIMINGS_COUNT] = {
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
};
|
||||
|
||||
#define IOPROX_XSF_TEST_DATA \
|
||||
{ 0x65, 0x01, 0x05, 0x39 }
|
||||
#define IOPROX_XSF_TEST_DATA_SIZE 4
|
||||
#define IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT (468 * 2)
|
||||
|
||||
const int8_t ioprox_xsf_test_timings[IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT] = {
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4,
|
||||
4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5,
|
||||
};
|
||||
|
||||
#define INDALA26_EMULATION_TIMINGS_COUNT (1024 * 2)
|
||||
#define INDALA26_TEST_DATA \
|
||||
{ 0x3B, 0x73, 0x64, 0xA8 }
|
||||
#define INDALA26_TEST_DATA_SIZE 4
|
||||
|
||||
const int8_t indala26_test_timings[INDALA26_EMULATION_TIMINGS_COUNT] = {
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1,
|
||||
1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1,
|
||||
-1, 1, -1, 1, -1, 1, -1, 1,
|
||||
};
|
||||
|
||||
MU_TEST(test_lfrfid_protocol_em_read_simple) {
|
||||
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
mu_assert_int_eq(EM_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolEM4100));
|
||||
mu_assert_string_eq("EM4100", protocol_dict_get_name(dict, LFRFIDProtocolEM4100));
|
||||
mu_assert_string_eq("EM-Micro", protocol_dict_get_manufacturer(dict, LFRFIDProtocolEM4100));
|
||||
|
||||
const uint8_t data[EM_TEST_DATA_SIZE] = EM_TEST_DATA;
|
||||
|
||||
protocol_dict_decoders_start(dict);
|
||||
|
||||
ProtocolId protocol = PROTOCOL_NO;
|
||||
PulseGlue* pulse_glue = pulse_glue_alloc();
|
||||
|
||||
for(size_t i = 0; i < EM_TEST_EMULATION_TIMINGS_COUNT * 10; i++) {
|
||||
bool pulse_pop = pulse_glue_push(
|
||||
pulse_glue,
|
||||
em_test_timings[i % EM_TEST_EMULATION_TIMINGS_COUNT] >= 0,
|
||||
abs(em_test_timings[i % EM_TEST_EMULATION_TIMINGS_COUNT]) *
|
||||
LF_RFID_READ_TIMING_MULTIPLIER);
|
||||
|
||||
if(pulse_pop) {
|
||||
uint32_t length, period;
|
||||
pulse_glue_pop(pulse_glue, &length, &period);
|
||||
|
||||
protocol = protocol_dict_decoders_feed(dict, true, period);
|
||||
if(protocol != PROTOCOL_NO) break;
|
||||
|
||||
protocol = protocol_dict_decoders_feed(dict, false, length - period);
|
||||
if(protocol != PROTOCOL_NO) break;
|
||||
}
|
||||
}
|
||||
|
||||
pulse_glue_free(pulse_glue);
|
||||
|
||||
mu_assert_int_eq(LFRFIDProtocolEM4100, protocol);
|
||||
uint8_t received_data[EM_TEST_DATA_SIZE] = {0};
|
||||
protocol_dict_get_data(dict, protocol, received_data, EM_TEST_DATA_SIZE);
|
||||
|
||||
mu_assert_mem_eq(data, received_data, EM_TEST_DATA_SIZE);
|
||||
|
||||
protocol_dict_free(dict);
|
||||
}
|
||||
|
||||
MU_TEST(test_lfrfid_protocol_em_emulate_simple) {
|
||||
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
mu_assert_int_eq(EM_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolEM4100));
|
||||
mu_assert_string_eq("EM4100", protocol_dict_get_name(dict, LFRFIDProtocolEM4100));
|
||||
mu_assert_string_eq("EM-Micro", protocol_dict_get_manufacturer(dict, LFRFIDProtocolEM4100));
|
||||
|
||||
const uint8_t data[EM_TEST_DATA_SIZE] = EM_TEST_DATA;
|
||||
|
||||
protocol_dict_set_data(dict, LFRFIDProtocolEM4100, data, EM_TEST_DATA_SIZE);
|
||||
mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolEM4100));
|
||||
|
||||
for(size_t i = 0; i < EM_TEST_EMULATION_TIMINGS_COUNT; i++) {
|
||||
LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolEM4100);
|
||||
|
||||
if(level_duration_get_level(level_duration)) {
|
||||
mu_assert_int_eq(em_test_timings[i], level_duration_get_duration(level_duration));
|
||||
} else {
|
||||
mu_assert_int_eq(em_test_timings[i], -level_duration_get_duration(level_duration));
|
||||
}
|
||||
}
|
||||
|
||||
protocol_dict_free(dict);
|
||||
}
|
||||
|
||||
MU_TEST(test_lfrfid_protocol_h10301_read_simple) {
|
||||
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
mu_assert_int_eq(
|
||||
HID10301_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolH10301));
|
||||
mu_assert_string_eq("H10301", protocol_dict_get_name(dict, LFRFIDProtocolH10301));
|
||||
mu_assert_string_eq("HID", protocol_dict_get_manufacturer(dict, LFRFIDProtocolH10301));
|
||||
|
||||
const uint8_t data[HID10301_TEST_DATA_SIZE] = HID10301_TEST_DATA;
|
||||
|
||||
protocol_dict_decoders_start(dict);
|
||||
|
||||
ProtocolId protocol = PROTOCOL_NO;
|
||||
PulseGlue* pulse_glue = pulse_glue_alloc();
|
||||
|
||||
for(size_t i = 0; i < HID10301_TEST_EMULATION_TIMINGS_COUNT * 10; i++) {
|
||||
bool pulse_pop = pulse_glue_push(
|
||||
pulse_glue,
|
||||
hid10301_test_timings[i % HID10301_TEST_EMULATION_TIMINGS_COUNT] >= 0,
|
||||
abs(hid10301_test_timings[i % HID10301_TEST_EMULATION_TIMINGS_COUNT]) *
|
||||
LF_RFID_READ_TIMING_MULTIPLIER);
|
||||
|
||||
if(pulse_pop) {
|
||||
uint32_t length, period;
|
||||
pulse_glue_pop(pulse_glue, &length, &period);
|
||||
|
||||
protocol = protocol_dict_decoders_feed(dict, true, period);
|
||||
if(protocol != PROTOCOL_NO) break;
|
||||
|
||||
protocol = protocol_dict_decoders_feed(dict, false, length - period);
|
||||
if(protocol != PROTOCOL_NO) break;
|
||||
}
|
||||
}
|
||||
|
||||
pulse_glue_free(pulse_glue);
|
||||
|
||||
mu_assert_int_eq(LFRFIDProtocolH10301, protocol);
|
||||
uint8_t received_data[HID10301_TEST_DATA_SIZE] = {0};
|
||||
protocol_dict_get_data(dict, protocol, received_data, HID10301_TEST_DATA_SIZE);
|
||||
|
||||
mu_assert_mem_eq(data, received_data, HID10301_TEST_DATA_SIZE);
|
||||
|
||||
protocol_dict_free(dict);
|
||||
}
|
||||
|
||||
MU_TEST(test_lfrfid_protocol_h10301_emulate_simple) {
|
||||
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
mu_assert_int_eq(
|
||||
HID10301_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolH10301));
|
||||
mu_assert_string_eq("H10301", protocol_dict_get_name(dict, LFRFIDProtocolH10301));
|
||||
mu_assert_string_eq("HID", protocol_dict_get_manufacturer(dict, LFRFIDProtocolH10301));
|
||||
|
||||
const uint8_t data[HID10301_TEST_DATA_SIZE] = HID10301_TEST_DATA;
|
||||
|
||||
protocol_dict_set_data(dict, LFRFIDProtocolH10301, data, HID10301_TEST_DATA_SIZE);
|
||||
mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolH10301));
|
||||
|
||||
for(size_t i = 0; i < HID10301_TEST_EMULATION_TIMINGS_COUNT; i++) {
|
||||
LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolH10301);
|
||||
|
||||
if(level_duration_get_level(level_duration)) {
|
||||
mu_assert_int_eq(
|
||||
hid10301_test_timings[i], level_duration_get_duration(level_duration));
|
||||
} else {
|
||||
mu_assert_int_eq(
|
||||
hid10301_test_timings[i], -level_duration_get_duration(level_duration));
|
||||
}
|
||||
}
|
||||
|
||||
protocol_dict_free(dict);
|
||||
}
|
||||
|
||||
MU_TEST(test_lfrfid_protocol_ioprox_xsf_read_simple) {
|
||||
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
mu_assert_int_eq(
|
||||
IOPROX_XSF_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIOProxXSF));
|
||||
mu_assert_string_eq("IoProxXSF", protocol_dict_get_name(dict, LFRFIDProtocolIOProxXSF));
|
||||
mu_assert_string_eq("Kantech", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIOProxXSF));
|
||||
|
||||
const uint8_t data[IOPROX_XSF_TEST_DATA_SIZE] = IOPROX_XSF_TEST_DATA;
|
||||
|
||||
protocol_dict_decoders_start(dict);
|
||||
|
||||
ProtocolId protocol = PROTOCOL_NO;
|
||||
PulseGlue* pulse_glue = pulse_glue_alloc();
|
||||
|
||||
for(size_t i = 0; i < IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT * 10; i++) {
|
||||
bool pulse_pop = pulse_glue_push(
|
||||
pulse_glue,
|
||||
ioprox_xsf_test_timings[i % IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT] >= 0,
|
||||
abs(ioprox_xsf_test_timings[i % IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT]) *
|
||||
LF_RFID_READ_TIMING_MULTIPLIER);
|
||||
|
||||
if(pulse_pop) {
|
||||
uint32_t length, period;
|
||||
pulse_glue_pop(pulse_glue, &length, &period);
|
||||
|
||||
protocol = protocol_dict_decoders_feed(dict, true, period);
|
||||
if(protocol != PROTOCOL_NO) break;
|
||||
|
||||
protocol = protocol_dict_decoders_feed(dict, false, length - period);
|
||||
if(protocol != PROTOCOL_NO) break;
|
||||
}
|
||||
}
|
||||
|
||||
pulse_glue_free(pulse_glue);
|
||||
|
||||
mu_assert_int_eq(LFRFIDProtocolIOProxXSF, protocol);
|
||||
uint8_t received_data[IOPROX_XSF_TEST_DATA_SIZE] = {0};
|
||||
protocol_dict_get_data(dict, protocol, received_data, IOPROX_XSF_TEST_DATA_SIZE);
|
||||
|
||||
mu_assert_mem_eq(data, received_data, IOPROX_XSF_TEST_DATA_SIZE);
|
||||
|
||||
protocol_dict_free(dict);
|
||||
}
|
||||
|
||||
MU_TEST(test_lfrfid_protocol_ioprox_xsf_emulate_simple) {
|
||||
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
mu_assert_int_eq(
|
||||
IOPROX_XSF_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIOProxXSF));
|
||||
mu_assert_string_eq("IoProxXSF", protocol_dict_get_name(dict, LFRFIDProtocolIOProxXSF));
|
||||
mu_assert_string_eq("Kantech", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIOProxXSF));
|
||||
|
||||
const uint8_t data[IOPROX_XSF_TEST_DATA_SIZE] = IOPROX_XSF_TEST_DATA;
|
||||
|
||||
protocol_dict_set_data(dict, LFRFIDProtocolIOProxXSF, data, IOPROX_XSF_TEST_DATA_SIZE);
|
||||
mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolIOProxXSF));
|
||||
|
||||
for(size_t i = 0; i < IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT; i++) {
|
||||
LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolIOProxXSF);
|
||||
|
||||
if(level_duration_get_level(level_duration)) {
|
||||
mu_assert_int_eq(
|
||||
ioprox_xsf_test_timings[i], level_duration_get_duration(level_duration));
|
||||
} else {
|
||||
mu_assert_int_eq(
|
||||
ioprox_xsf_test_timings[i], -level_duration_get_duration(level_duration));
|
||||
}
|
||||
}
|
||||
|
||||
protocol_dict_free(dict);
|
||||
}
|
||||
|
||||
MU_TEST(test_lfrfid_protocol_inadala26_emulate_simple) {
|
||||
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
mu_assert_int_eq(
|
||||
INDALA26_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIndala26));
|
||||
mu_assert_string_eq("Indala26", protocol_dict_get_name(dict, LFRFIDProtocolIndala26));
|
||||
mu_assert_string_eq("Motorola", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIndala26));
|
||||
|
||||
const uint8_t data[INDALA26_TEST_DATA_SIZE] = INDALA26_TEST_DATA;
|
||||
|
||||
protocol_dict_set_data(dict, LFRFIDProtocolIndala26, data, INDALA26_TEST_DATA_SIZE);
|
||||
mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolIndala26));
|
||||
|
||||
for(size_t i = 0; i < INDALA26_EMULATION_TIMINGS_COUNT; i++) {
|
||||
LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolIndala26);
|
||||
|
||||
if(level_duration_get_level(level_duration)) {
|
||||
mu_assert_int_eq(
|
||||
indala26_test_timings[i], level_duration_get_duration(level_duration));
|
||||
} else {
|
||||
mu_assert_int_eq(
|
||||
indala26_test_timings[i], -level_duration_get_duration(level_duration));
|
||||
}
|
||||
}
|
||||
|
||||
protocol_dict_free(dict);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(test_lfrfid_protocols_suite) {
|
||||
MU_RUN_TEST(test_lfrfid_protocol_em_read_simple);
|
||||
MU_RUN_TEST(test_lfrfid_protocol_em_emulate_simple);
|
||||
|
||||
MU_RUN_TEST(test_lfrfid_protocol_h10301_read_simple);
|
||||
MU_RUN_TEST(test_lfrfid_protocol_h10301_emulate_simple);
|
||||
|
||||
MU_RUN_TEST(test_lfrfid_protocol_ioprox_xsf_read_simple);
|
||||
MU_RUN_TEST(test_lfrfid_protocol_ioprox_xsf_emulate_simple);
|
||||
|
||||
MU_RUN_TEST(test_lfrfid_protocol_inadala26_emulate_simple);
|
||||
}
|
||||
|
||||
int run_minunit_test_lfrfid_protocols() {
|
||||
MU_RUN_SUITE(test_lfrfid_protocols_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
656
applications/debug/unit_tests/minunit.h
Normal file
656
applications/debug/unit_tests/minunit.h
Normal file
@@ -0,0 +1,656 @@
|
||||
/*
|
||||
* Copyright (c) 2012 David Siñuela Pastor, siu.4coders@gmail.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MINUNIT_MINUNIT_H
|
||||
#define MINUNIT_MINUNIT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <Windows.h>
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
#define __func__ __FUNCTION__
|
||||
#endif
|
||||
|
||||
#elif defined(__unix__) || defined(__unix) || defined(unix) || \
|
||||
(defined(__APPLE__) && defined(__MACH__))
|
||||
|
||||
/* Change POSIX C SOURCE version for pure c99 compilers */
|
||||
#if !defined(_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 200112L
|
||||
#undef _POSIX_C_SOURCE
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
#endif
|
||||
|
||||
#include <unistd.h> /* POSIX flags */
|
||||
#include <time.h> /* clock_gettime(), time() */
|
||||
#include <sys/time.h> /* gethrtime(), gettimeofday() */
|
||||
#include <sys/resource.h>
|
||||
#include <sys/times.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__MACH__) && defined(__APPLE__)
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
#endif
|
||||
|
||||
#if __GNUC__ >= 5 && !defined(__STDC_VERSION__)
|
||||
#define __func__ __extension__ __FUNCTION__
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
// #error "Unable to define timers for an unknown OS."
|
||||
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
/* Maximum length of last message */
|
||||
#define MINUNIT_MESSAGE_LEN 1024
|
||||
/* Accuracy with which floats are compared */
|
||||
#define MINUNIT_EPSILON 1E-12
|
||||
|
||||
#include "minunit_vars_ex.h"
|
||||
|
||||
/* Test setup and teardown function pointers */
|
||||
__attribute__((unused)) static void (*minunit_setup)(void) = NULL;
|
||||
__attribute__((unused)) static void (*minunit_teardown)(void) = NULL;
|
||||
|
||||
void minunit_print_progress(void);
|
||||
void minunit_print_fail(const char* error);
|
||||
|
||||
/* Definitions */
|
||||
#define MU_TEST(method_name) static void method_name(void)
|
||||
#define MU_TEST_1(method_name, arg_1) static void method_name(arg_1)
|
||||
#define MU_TEST_SUITE(suite_name) static void suite_name(void)
|
||||
|
||||
#define MU__SAFE_BLOCK(block) \
|
||||
do { \
|
||||
block \
|
||||
} while(0)
|
||||
|
||||
/* Run test suite and unset setup and teardown functions */
|
||||
#define MU_RUN_SUITE(suite_name) \
|
||||
MU__SAFE_BLOCK(suite_name(); minunit_setup = NULL; minunit_teardown = NULL;)
|
||||
|
||||
/* Configure setup and teardown functions */
|
||||
#define MU_SUITE_CONFIGURE(setup_fun, teardown_fun) \
|
||||
MU__SAFE_BLOCK(minunit_setup = setup_fun; minunit_teardown = teardown_fun;)
|
||||
|
||||
/* Test runner */
|
||||
#define MU_RUN_TEST(test) \
|
||||
MU__SAFE_BLOCK( \
|
||||
if(minunit_real_timer == 0 && minunit_proc_timer == 0) { \
|
||||
minunit_real_timer = mu_timer_real(); \
|
||||
minunit_proc_timer = mu_timer_cpu(); \
|
||||
} if(minunit_setup) (*minunit_setup)(); \
|
||||
minunit_status = 0; \
|
||||
printf(#test "()\r\n"); \
|
||||
test(); \
|
||||
minunit_run++; \
|
||||
if(minunit_status) { \
|
||||
minunit_fail++; \
|
||||
minunit_print_fail(minunit_last_message); \
|
||||
minunit_status = 0; \
|
||||
} fflush(stdout); \
|
||||
if(minunit_teardown)(*minunit_teardown)();)
|
||||
|
||||
#define MU_RUN_TEST_1(test, arg_1) \
|
||||
MU__SAFE_BLOCK( \
|
||||
if(minunit_real_timer == 0 && minunit_proc_timer == 0) { \
|
||||
minunit_real_timer = mu_timer_real(); \
|
||||
minunit_proc_timer = mu_timer_cpu(); \
|
||||
} if(minunit_setup) (*minunit_setup)(); \
|
||||
minunit_status = 0; \
|
||||
printf(#test "(" #arg_1 ")\r\n"); \
|
||||
test(arg_1); \
|
||||
minunit_run++; \
|
||||
if(minunit_status) { \
|
||||
minunit_fail++; \
|
||||
minunit_print_fail(minunit_last_message); \
|
||||
minunit_status = 0; \
|
||||
} fflush(stdout); \
|
||||
if(minunit_teardown)(*minunit_teardown)();)
|
||||
|
||||
/* Report */
|
||||
#define MU_REPORT() \
|
||||
MU__SAFE_BLOCK(double minunit_end_real_timer; double minunit_end_proc_timer; printf( \
|
||||
"\n\n%d tests, %d assertions, %d failures\n", \
|
||||
minunit_run, \
|
||||
minunit_assert, \
|
||||
minunit_fail); \
|
||||
minunit_end_real_timer = mu_timer_real(); \
|
||||
minunit_end_proc_timer = mu_timer_cpu(); \
|
||||
printf( \
|
||||
"\nFinished in %.8f seconds (real) %.8f seconds (proc)\n\n", \
|
||||
minunit_end_real_timer - minunit_real_timer, \
|
||||
minunit_end_proc_timer - minunit_proc_timer);)
|
||||
#define MU_EXIT_CODE minunit_fail
|
||||
|
||||
/* Assertions */
|
||||
#define mu_check(test) \
|
||||
MU__SAFE_BLOCK( \
|
||||
minunit_assert++; if(!(test)) { \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: %s", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
#test); \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
} else { minunit_print_progress(); })
|
||||
|
||||
#define mu_fail(message) \
|
||||
MU__SAFE_BLOCK(minunit_assert++; snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: %s", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
message); \
|
||||
minunit_status = 1; \
|
||||
return;)
|
||||
|
||||
#define mu_assert(test, message) \
|
||||
MU__SAFE_BLOCK( \
|
||||
minunit_assert++; if(!(test)) { \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: %s", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
message); \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
} else { minunit_print_progress(); })
|
||||
|
||||
#define mu_assert_int_eq(expected, result) \
|
||||
MU__SAFE_BLOCK( \
|
||||
int minunit_tmp_e; int minunit_tmp_r; minunit_assert++; minunit_tmp_e = (expected); \
|
||||
minunit_tmp_r = (result); \
|
||||
if(minunit_tmp_e != minunit_tmp_r) { \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: %d expected but was %d", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
minunit_tmp_e, \
|
||||
minunit_tmp_r); \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
} else { minunit_print_progress(); })
|
||||
|
||||
#define mu_assert_int_not_eq(expected, result) \
|
||||
MU__SAFE_BLOCK( \
|
||||
int minunit_tmp_e; int minunit_tmp_r; minunit_assert++; minunit_tmp_e = (expected); \
|
||||
minunit_tmp_r = (result); \
|
||||
if(minunit_tmp_e == minunit_tmp_r) { \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: expected different results but both were %d", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
minunit_tmp_e); \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
} else { minunit_print_progress(); })
|
||||
|
||||
#define mu_assert_int_greater_than(val, result) \
|
||||
MU__SAFE_BLOCK( \
|
||||
int minunit_tmp_e; int minunit_tmp_r; minunit_assert++; minunit_tmp_e = (val); \
|
||||
minunit_tmp_r = (result); \
|
||||
if(val >= minunit_tmp_r) { \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: %d <= %d", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
minunit_tmp_r, \
|
||||
minunit_tmp_e); \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
} else { minunit_print_progress(); })
|
||||
|
||||
#define mu_assert_int_less_than(val, result) \
|
||||
MU__SAFE_BLOCK( \
|
||||
int minunit_tmp_e; int minunit_tmp_r; minunit_assert++; minunit_tmp_e = (val); \
|
||||
minunit_tmp_r = (result); \
|
||||
if(val <= minunit_tmp_r) { \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: %d >= %d", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
minunit_tmp_r, \
|
||||
minunit_tmp_e); \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
} else { minunit_print_progress(); })
|
||||
|
||||
#define mu_assert_int_between(expected_lower, expected_upper, result) \
|
||||
MU__SAFE_BLOCK( \
|
||||
int minunit_tmp_e; int minunit_tmp_m; int minunit_tmp_r; minunit_assert++; \
|
||||
minunit_tmp_e = (expected_lower); \
|
||||
minunit_tmp_m = (expected_upper); \
|
||||
minunit_tmp_r = (result); \
|
||||
if(result < minunit_tmp_e || result > minunit_tmp_m) { \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: %d was not between (inclusive) %d and %d", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
minunit_tmp_e, \
|
||||
minunit_tmp_r, \
|
||||
minunit_tmp_m); \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
} else { minunit_print_progress(); })
|
||||
|
||||
#define mu_assert_int_in(expected, array_length, result) \
|
||||
MU__SAFE_BLOCK( \
|
||||
int minunit_tmp_r; minunit_assert++; minunit_tmp_r = (result); int t = 0; int i; \
|
||||
for(i = 0; i < array_length; i++) { \
|
||||
if(expected[i] == minunit_tmp_r) t = 1; \
|
||||
} if(t == 0) { \
|
||||
char tmp[500] = {0}; \
|
||||
tmp[0] = '['; \
|
||||
for(i = 0; i < array_length; i++) { \
|
||||
sprintf(tmp + strlen(tmp), "%d, ", expected[i]); \
|
||||
} \
|
||||
int len = strlen(tmp); \
|
||||
tmp[len - 2] = ']'; \
|
||||
tmp[len - 1] = '\0'; \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: expected to be one of %s but was %d", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
tmp, \
|
||||
minunit_tmp_r); \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
} else { minunit_print_progress(); })
|
||||
|
||||
#define mu_assert_double_eq(expected, result) \
|
||||
MU__SAFE_BLOCK( \
|
||||
double minunit_tmp_e; double minunit_tmp_r; minunit_assert++; minunit_tmp_e = (expected); \
|
||||
minunit_tmp_r = (result); \
|
||||
if(fabs(minunit_tmp_e - minunit_tmp_r) > MINUNIT_EPSILON) { \
|
||||
int minunit_significant_figures = 1 - log10(MINUNIT_EPSILON); \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: %.*g expected but was %.*g", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
minunit_significant_figures, \
|
||||
minunit_tmp_e, \
|
||||
minunit_significant_figures, \
|
||||
minunit_tmp_r); \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
} else { minunit_print_progress(); })
|
||||
|
||||
#define mu_assert_double_greater_than(val, result) \
|
||||
MU__SAFE_BLOCK( \
|
||||
double minunit_tmp_e; double minunit_tmp_r; minunit_assert++; minunit_tmp_e = (val); \
|
||||
minunit_tmp_r = (result); \
|
||||
if(val >= minunit_tmp_r) { \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: %f <= %f", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
minunit_tmp_r, \
|
||||
minunit_tmp_e); \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
} else { minunit_print_progress(); })
|
||||
|
||||
#define mu_assert_double_less_than(val, result) \
|
||||
MU__SAFE_BLOCK( \
|
||||
double minunit_tmp_e; double minunit_tmp_r; minunit_assert++; minunit_tmp_e = (val); \
|
||||
minunit_tmp_r = (result); \
|
||||
if(val <= minunit_tmp_r) { \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: %f >= %f", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
minunit_tmp_r, \
|
||||
minunit_tmp_e); \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
} else { minunit_print_progress(); })
|
||||
|
||||
#define mu_assert_double_between(expected_lower, expected_upper, result) \
|
||||
MU__SAFE_BLOCK( \
|
||||
double minunit_tmp_e; double minunit_tmp_m; double minunit_tmp_r; minunit_assert++; \
|
||||
minunit_tmp_e = (expected_lower); \
|
||||
minunit_tmp_m = (expected_upper); \
|
||||
minunit_tmp_r = (result); \
|
||||
if(result < minunit_tmp_e || result > minunit_tmp_m) { \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: %f was not between (inclusive) %f and %f", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
minunit_tmp_e, \
|
||||
minunit_tmp_r, \
|
||||
minunit_tmp_m); \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
} else { minunit_print_progress(); })
|
||||
|
||||
#define mu_assert_string_eq(expected, result) \
|
||||
MU__SAFE_BLOCK( \
|
||||
const char* minunit_tmp_e = expected; const char* minunit_tmp_r = result; \
|
||||
minunit_assert++; \
|
||||
if(!minunit_tmp_e) { minunit_tmp_e = "<null pointer>"; } if(!minunit_tmp_r) { \
|
||||
minunit_tmp_r = "<null pointer>"; \
|
||||
} if(strcmp(minunit_tmp_e, minunit_tmp_r)) { \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: '%s' expected but was '%s'", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
minunit_tmp_e, \
|
||||
minunit_tmp_r); \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
} else { minunit_print_progress(); })
|
||||
|
||||
#define mu_assert_mem_eq(expected, result, size) \
|
||||
MU__SAFE_BLOCK( \
|
||||
const void* minunit_tmp_e = expected; const void* minunit_tmp_r = result; \
|
||||
minunit_assert++; \
|
||||
if(memcmp(minunit_tmp_e, minunit_tmp_r, size)) { \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: mem not equal\r\n\tEXP RES", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__); \
|
||||
for(size_t __index = 0; __index < size; __index++) { \
|
||||
if(strlen(minunit_last_message) > MINUNIT_MESSAGE_LEN - 20) break; \
|
||||
uint8_t __e = ((uint8_t*)minunit_tmp_e)[__index]; \
|
||||
uint8_t __r = ((uint8_t*)minunit_tmp_r)[__index]; \
|
||||
snprintf( \
|
||||
minunit_last_message + strlen(minunit_last_message), \
|
||||
MINUNIT_MESSAGE_LEN - strlen(minunit_last_message), \
|
||||
"\r\n\t%02X %s %02X", \
|
||||
__e, \
|
||||
((__e == __r) ? ".." : "!="), \
|
||||
__r); \
|
||||
} \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
} else { minunit_print_progress(); })
|
||||
|
||||
#define mu_assert_null(result) \
|
||||
MU__SAFE_BLOCK( \
|
||||
minunit_assert++; if(result == NULL) { minunit_print_progress(); } else { \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: Expected result was not NULL", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__); \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
})
|
||||
|
||||
#define mu_assert_not_null(result) \
|
||||
MU__SAFE_BLOCK( \
|
||||
minunit_assert++; if(result != NULL) { minunit_print_progress(); } else { \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: Expected result was not NULL", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__); \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
})
|
||||
|
||||
#define mu_assert_pointers_eq(pointer1, pointer2) \
|
||||
MU__SAFE_BLOCK( \
|
||||
minunit_assert++; if(pointer1 == pointer2) { minunit_print_progress(); } else { \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: Expected the pointers to point to the same memory location", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__); \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
})
|
||||
|
||||
#define mu_assert_pointers_not_eq(pointer1, pointer2) \
|
||||
MU__SAFE_BLOCK( \
|
||||
minunit_assert++; if(pointer1 != pointer2) { minunit_print_progress(); } else { \
|
||||
snprintf( \
|
||||
minunit_last_message, \
|
||||
MINUNIT_MESSAGE_LEN, \
|
||||
"%s failed:\r\n\t%s:%d: Expected the pointers to point to the same memory location", \
|
||||
__func__, \
|
||||
__FILE__, \
|
||||
__LINE__); \
|
||||
minunit_status = 1; \
|
||||
return; \
|
||||
})
|
||||
|
||||
/*
|
||||
* The following two functions were written by David Robert Nadeau
|
||||
* from http://NadeauSoftware.com/ and distributed under the
|
||||
* Creative Commons Attribution 3.0 Unported License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the real time, in seconds, or -1.0 if an error occurred.
|
||||
*
|
||||
* Time is measured since an arbitrary and OS-dependent start time.
|
||||
* The returned real time is only useful for computing an elapsed time
|
||||
* between two calls to this function.
|
||||
*/
|
||||
__attribute__((unused)) static double mu_timer_real(void) {
|
||||
#if defined(_WIN32)
|
||||
/* Windows 2000 and later. ---------------------------------- */
|
||||
LARGE_INTEGER Time;
|
||||
LARGE_INTEGER Frequency;
|
||||
|
||||
QueryPerformanceFrequency(&Frequency);
|
||||
QueryPerformanceCounter(&Time);
|
||||
|
||||
Time.QuadPart *= 1000000;
|
||||
Time.QuadPart /= Frequency.QuadPart;
|
||||
|
||||
return (double)Time.QuadPart / 1000000.0;
|
||||
|
||||
#elif(defined(__hpux) || defined(hpux)) || \
|
||||
((defined(__sun__) || defined(__sun) || defined(sun)) && \
|
||||
(defined(__SVR4) || defined(__svr4__)))
|
||||
/* HP-UX, Solaris. ------------------------------------------ */
|
||||
return (double)gethrtime() / 1000000000.0;
|
||||
|
||||
#elif defined(__MACH__) && defined(__APPLE__)
|
||||
/* OSX. ----------------------------------------------------- */
|
||||
static double timeConvert = 0.0;
|
||||
if(timeConvert == 0.0) {
|
||||
mach_timebase_info_data_t timeBase;
|
||||
(void)mach_timebase_info(&timeBase);
|
||||
timeConvert = (double)timeBase.numer / (double)timeBase.denom / 1000000000.0;
|
||||
}
|
||||
return (double)mach_absolute_time() * timeConvert;
|
||||
|
||||
#elif defined(_POSIX_VERSION)
|
||||
/* POSIX. --------------------------------------------------- */
|
||||
struct timeval tm;
|
||||
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
|
||||
{
|
||||
struct timespec ts;
|
||||
#if defined(CLOCK_MONOTONIC_PRECISE)
|
||||
/* BSD. --------------------------------------------- */
|
||||
const clockid_t id = CLOCK_MONOTONIC_PRECISE;
|
||||
#elif defined(CLOCK_MONOTONIC_RAW)
|
||||
/* Linux. ------------------------------------------- */
|
||||
const clockid_t id = CLOCK_MONOTONIC_RAW;
|
||||
#elif defined(CLOCK_HIGHRES)
|
||||
/* Solaris. ----------------------------------------- */
|
||||
const clockid_t id = CLOCK_HIGHRES;
|
||||
#elif defined(CLOCK_MONOTONIC)
|
||||
/* AIX, BSD, Linux, POSIX, Solaris. ----------------- */
|
||||
const clockid_t id = CLOCK_MONOTONIC;
|
||||
#elif defined(CLOCK_REALTIME)
|
||||
/* AIX, BSD, HP-UX, Linux, POSIX. ------------------- */
|
||||
const clockid_t id = CLOCK_REALTIME;
|
||||
#else
|
||||
const clockid_t id = (clockid_t)-1; /* Unknown. */
|
||||
#endif /* CLOCK_* */
|
||||
if(id != (clockid_t)-1 && clock_gettime(id, &ts) != -1)
|
||||
return (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0;
|
||||
/* Fall thru. */
|
||||
}
|
||||
#endif /* _POSIX_TIMERS */
|
||||
|
||||
/* AIX, BSD, Cygwin, HP-UX, Linux, OSX, POSIX, Solaris. ----- */
|
||||
gettimeofday(&tm, NULL);
|
||||
return (double)tm.tv_sec + (double)tm.tv_usec / 1000000.0;
|
||||
#else
|
||||
return -1.0; /* Failed. */
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of CPU time used by the current process,
|
||||
* in seconds, or -1.0 if an error occurred.
|
||||
*/
|
||||
__attribute__((unused)) static double mu_timer_cpu(void) {
|
||||
#if defined(_WIN32)
|
||||
/* Windows -------------------------------------------------- */
|
||||
FILETIME createTime;
|
||||
FILETIME exitTime;
|
||||
FILETIME kernelTime;
|
||||
FILETIME userTime;
|
||||
|
||||
/* This approach has a resolution of 1/64 second. Unfortunately, Windows' API does not offer better */
|
||||
if(GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime) != 0) {
|
||||
ULARGE_INTEGER userSystemTime;
|
||||
memcpy(&userSystemTime, &userTime, sizeof(ULARGE_INTEGER));
|
||||
return (double)userSystemTime.QuadPart / 10000000.0;
|
||||
}
|
||||
|
||||
#elif defined(__unix__) || defined(__unix) || defined(unix) || \
|
||||
(defined(__APPLE__) && defined(__MACH__))
|
||||
/* AIX, BSD, Cygwin, HP-UX, Linux, OSX, and Solaris --------- */
|
||||
|
||||
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
|
||||
/* Prefer high-res POSIX timers, when available. */
|
||||
{
|
||||
clockid_t id;
|
||||
struct timespec ts;
|
||||
#if _POSIX_CPUTIME > 0
|
||||
/* Clock ids vary by OS. Query the id, if possible. */
|
||||
if(clock_getcpuclockid(0, &id) == -1)
|
||||
#endif
|
||||
#if defined(CLOCK_PROCESS_CPUTIME_ID)
|
||||
/* Use known clock id for AIX, Linux, or Solaris. */
|
||||
id = CLOCK_PROCESS_CPUTIME_ID;
|
||||
#elif defined(CLOCK_VIRTUAL)
|
||||
/* Use known clock id for BSD or HP-UX. */
|
||||
id = CLOCK_VIRTUAL;
|
||||
#else
|
||||
id = (clockid_t)-1;
|
||||
#endif
|
||||
if(id != (clockid_t)-1 && clock_gettime(id, &ts) != -1)
|
||||
return (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(RUSAGE_SELF)
|
||||
{
|
||||
struct rusage rusage;
|
||||
if(getrusage(RUSAGE_SELF, &rusage) != -1)
|
||||
return (double)rusage.ru_utime.tv_sec + (double)rusage.ru_utime.tv_usec / 1000000.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_SC_CLK_TCK)
|
||||
{
|
||||
const double ticks = (double)sysconf(_SC_CLK_TCK);
|
||||
struct tms tms;
|
||||
if(times(&tms) != (clock_t)-1) return (double)tms.tms_utime / ticks;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CLOCKS_PER_SEC)
|
||||
{
|
||||
clock_t cl = clock();
|
||||
if(cl != (clock_t)-1) return (double)cl / (double)CLOCKS_PER_SEC;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
return -1; /* Failed. */
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MINUNIT_MINUNIT_H */
|
15
applications/debug/unit_tests/minunit_vars.h
Normal file
15
applications/debug/unit_tests/minunit_vars.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include "minunit.h"
|
||||
|
||||
/* Misc. counters */
|
||||
int minunit_run = 0;
|
||||
int minunit_assert = 0;
|
||||
int minunit_fail = 0;
|
||||
int minunit_status = 0;
|
||||
|
||||
/* Timers */
|
||||
double minunit_real_timer = 0;
|
||||
double minunit_proc_timer = 0;
|
||||
|
||||
/* Last message */
|
||||
char minunit_last_message[MINUNIT_MESSAGE_LEN];
|
15
applications/debug/unit_tests/minunit_vars_ex.h
Normal file
15
applications/debug/unit_tests/minunit_vars_ex.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include "minunit.h"
|
||||
|
||||
/* Misc. counters */
|
||||
extern int minunit_run;
|
||||
extern int minunit_assert;
|
||||
extern int minunit_fail;
|
||||
extern int minunit_status;
|
||||
|
||||
/* Timers */
|
||||
extern double minunit_real_timer;
|
||||
extern double minunit_proc_timer;
|
||||
|
||||
/* Last message */
|
||||
extern char minunit_last_message[MINUNIT_MESSAGE_LEN];
|
184
applications/debug/unit_tests/nfc/nfc_test.c
Normal file
184
applications/debug/unit_tests/nfc/nfc_test.c
Normal file
@@ -0,0 +1,184 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <storage/storage.h>
|
||||
#include <lib/flipper_format/flipper_format.h>
|
||||
#include <lib/nfc/protocols/nfca.h>
|
||||
#include <lib/digital_signal/digital_signal.h>
|
||||
|
||||
#include <lib/flipper_format/flipper_format_i.h>
|
||||
#include <lib/toolbox/stream/file_stream.h>
|
||||
|
||||
#include "../minunit.h"
|
||||
|
||||
#define TAG "NfcTest"
|
||||
|
||||
#define NFC_TEST_RESOURCES_DIR EXT_PATH("unit_tests/nfc/")
|
||||
#define NFC_TEST_SIGNAL_SHORT_FILE "nfc_nfca_signal_short.nfc"
|
||||
#define NFC_TEST_SIGNAL_LONG_FILE "nfc_nfca_signal_long.nfc"
|
||||
|
||||
static const char* nfc_test_file_type = "Flipper NFC test";
|
||||
static const uint32_t nfc_test_file_version = 1;
|
||||
|
||||
#define NFC_TEST_DATA_MAX_LEN 18
|
||||
#define NFC_TETS_TIMINGS_MAX_LEN 1350
|
||||
|
||||
typedef struct {
|
||||
Storage* storage;
|
||||
NfcaSignal* signal;
|
||||
uint32_t test_data_len;
|
||||
uint8_t test_data[NFC_TEST_DATA_MAX_LEN];
|
||||
uint32_t test_timings_len;
|
||||
uint32_t test_timings[NFC_TETS_TIMINGS_MAX_LEN];
|
||||
} NfcTest;
|
||||
|
||||
static NfcTest* nfc_test = NULL;
|
||||
|
||||
static void nfc_test_alloc() {
|
||||
nfc_test = malloc(sizeof(NfcTest));
|
||||
nfc_test->signal = nfca_signal_alloc();
|
||||
nfc_test->storage = furi_record_open(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void nfc_test_free() {
|
||||
furi_assert(nfc_test);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
nfca_signal_free(nfc_test->signal);
|
||||
free(nfc_test);
|
||||
nfc_test = NULL;
|
||||
}
|
||||
|
||||
static bool nfc_test_read_signal_from_file(const char* file_name) {
|
||||
bool success = false;
|
||||
|
||||
FlipperFormat* file = flipper_format_file_alloc(nfc_test->storage);
|
||||
string_t file_type;
|
||||
string_init(file_type);
|
||||
uint32_t file_version = 0;
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(file, file_name)) break;
|
||||
if(!flipper_format_read_header(file, file_type, &file_version)) break;
|
||||
if(string_cmp_str(file_type, nfc_test_file_type) || file_version != nfc_test_file_version)
|
||||
break;
|
||||
if(!flipper_format_read_uint32(file, "Data length", &nfc_test->test_data_len, 1)) break;
|
||||
if(nfc_test->test_data_len > NFC_TEST_DATA_MAX_LEN) break;
|
||||
if(!flipper_format_read_hex(
|
||||
file, "Plain data", nfc_test->test_data, nfc_test->test_data_len))
|
||||
break;
|
||||
if(!flipper_format_read_uint32(file, "Timings length", &nfc_test->test_timings_len, 1))
|
||||
break;
|
||||
if(nfc_test->test_timings_len > NFC_TETS_TIMINGS_MAX_LEN) break;
|
||||
if(!flipper_format_read_uint32(
|
||||
file, "Timings", nfc_test->test_timings, nfc_test->test_timings_len))
|
||||
break;
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
string_clear(file_type);
|
||||
flipper_format_free(file);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool nfc_test_digital_signal_test_encode(
|
||||
const char* file_name,
|
||||
uint32_t encode_max_time,
|
||||
uint32_t timing_tolerance,
|
||||
uint32_t timings_sum_tolerance) {
|
||||
furi_assert(nfc_test);
|
||||
|
||||
bool success = false;
|
||||
uint32_t time = 0;
|
||||
uint32_t dut_timings_sum = 0;
|
||||
uint32_t ref_timings_sum = 0;
|
||||
uint8_t parity[10] = {};
|
||||
|
||||
do {
|
||||
// Read test data
|
||||
if(!nfc_test_read_signal_from_file(file_name)) break;
|
||||
|
||||
// Encode signal
|
||||
FURI_CRITICAL_ENTER();
|
||||
time = DWT->CYCCNT;
|
||||
nfca_signal_encode(
|
||||
nfc_test->signal, nfc_test->test_data, nfc_test->test_data_len * 8, parity);
|
||||
digital_signal_prepare_arr(nfc_test->signal->tx_signal);
|
||||
time = (DWT->CYCCNT - time) / furi_hal_cortex_instructions_per_microsecond();
|
||||
FURI_CRITICAL_EXIT();
|
||||
|
||||
// Check timings
|
||||
if(time > encode_max_time) {
|
||||
FURI_LOG_E(
|
||||
TAG, "Encoding time: %d us while accepted value: %d us", time, encode_max_time);
|
||||
break;
|
||||
}
|
||||
|
||||
// Check data
|
||||
if(nfc_test->signal->tx_signal->edge_cnt != nfc_test->test_timings_len) {
|
||||
FURI_LOG_E(TAG, "Not equal timings buffers length");
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t timings_diff = 0;
|
||||
uint32_t* ref = nfc_test->test_timings;
|
||||
uint32_t* dut = nfc_test->signal->tx_signal->reload_reg_buff;
|
||||
bool timing_check_success = true;
|
||||
for(size_t i = 0; i < nfc_test->test_timings_len; i++) {
|
||||
timings_diff = dut[i] > ref[i] ? dut[i] - ref[i] : ref[i] - dut[i];
|
||||
dut_timings_sum += dut[i];
|
||||
ref_timings_sum += ref[i];
|
||||
if(timings_diff > timing_tolerance) {
|
||||
FURI_LOG_E(
|
||||
TAG, "Too big differece in %d timings. Ref: %d, DUT: %d", i, ref[i], dut[i]);
|
||||
timing_check_success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!timing_check_success) break;
|
||||
uint32_t sum_diff = dut_timings_sum > ref_timings_sum ? dut_timings_sum - ref_timings_sum :
|
||||
ref_timings_sum - dut_timings_sum;
|
||||
if(sum_diff > timings_sum_tolerance) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Too big difference in timings sum. Ref: %d, DUT: %d",
|
||||
ref_timings_sum,
|
||||
dut_timings_sum);
|
||||
break;
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "Encoding time: %d us. Acceptable time: %d us", time, encode_max_time);
|
||||
FURI_LOG_I(
|
||||
TAG,
|
||||
"Timings sum difference: %d [1/64MHZ]. Acceptable difference: %d [1/64MHz]",
|
||||
sum_diff,
|
||||
timings_sum_tolerance);
|
||||
success = true;
|
||||
} while(false);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
MU_TEST(nfc_digital_signal_test) {
|
||||
mu_assert(
|
||||
nfc_test_digital_signal_test_encode(
|
||||
NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_SHORT_FILE, 500, 1, 37),
|
||||
"NFC short digital signal test failed\r\n");
|
||||
mu_assert(
|
||||
nfc_test_digital_signal_test_encode(
|
||||
NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_LONG_FILE, 2000, 1, 37),
|
||||
"NFC long digital signal test failed\r\n");
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(nfc) {
|
||||
nfc_test_alloc();
|
||||
|
||||
MU_RUN_TEST(nfc_digital_signal_test);
|
||||
|
||||
nfc_test_free();
|
||||
}
|
||||
|
||||
int run_minunit_test_nfc() {
|
||||
MU_RUN_SUITE(nfc);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
222
applications/debug/unit_tests/protocol_dict/protocol_dict_test.c
Normal file
222
applications/debug/unit_tests/protocol_dict/protocol_dict_test.c
Normal file
@@ -0,0 +1,222 @@
|
||||
#include <furi.h>
|
||||
#include "../minunit.h"
|
||||
#include <toolbox/protocols/protocol_dict.h>
|
||||
|
||||
typedef enum {
|
||||
TestDictProtocol0,
|
||||
TestDictProtocol1,
|
||||
|
||||
TestDictProtocolMax,
|
||||
} TestDictProtocols;
|
||||
|
||||
/*********************** PROTOCOL 0 START ***********************/
|
||||
|
||||
typedef struct {
|
||||
uint32_t data;
|
||||
size_t encoder_counter;
|
||||
} Protocol0Data;
|
||||
|
||||
static const uint32_t protocol_0_decoder_result = 0xDEADBEEF;
|
||||
|
||||
static void* protocol_0_alloc() {
|
||||
void* data = malloc(sizeof(Protocol0Data));
|
||||
return data;
|
||||
}
|
||||
|
||||
static void protocol_0_free(Protocol0Data* data) {
|
||||
free(data);
|
||||
}
|
||||
|
||||
static uint8_t* protocol_0_get_data(Protocol0Data* data) {
|
||||
return (uint8_t*)&data->data;
|
||||
}
|
||||
|
||||
static void protocol_0_decoder_start(Protocol0Data* data) {
|
||||
data->data = 0;
|
||||
}
|
||||
|
||||
static bool protocol_0_decoder_feed(Protocol0Data* data, bool level, uint32_t duration) {
|
||||
if(level && duration == 666) {
|
||||
data->data = protocol_0_decoder_result;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool protocol_0_encoder_start(Protocol0Data* data) {
|
||||
data->encoder_counter = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static LevelDuration protocol_0_encoder_yield(Protocol0Data* data) {
|
||||
data->encoder_counter++;
|
||||
return level_duration_make(data->encoder_counter % 2, data->data);
|
||||
}
|
||||
|
||||
/*********************** PROTOCOL 1 START ***********************/
|
||||
|
||||
typedef struct {
|
||||
uint64_t data;
|
||||
size_t encoder_counter;
|
||||
} Protocol1Data;
|
||||
|
||||
static const uint64_t protocol_1_decoder_result = 0x1234567890ABCDEF;
|
||||
|
||||
static void* protocol_1_alloc() {
|
||||
void* data = malloc(sizeof(Protocol1Data));
|
||||
return data;
|
||||
}
|
||||
|
||||
static void protocol_1_free(Protocol1Data* data) {
|
||||
free(data);
|
||||
}
|
||||
|
||||
static uint8_t* protocol_1_get_data(Protocol1Data* data) {
|
||||
return (uint8_t*)&data->data;
|
||||
}
|
||||
|
||||
static void protocol_1_decoder_start(Protocol1Data* data) {
|
||||
data->data = 0;
|
||||
}
|
||||
|
||||
static bool protocol_1_decoder_feed(Protocol1Data* data, bool level, uint32_t duration) {
|
||||
if(level && duration == 543) {
|
||||
data->data = 0x1234567890ABCDEF;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool protocol_1_encoder_start(Protocol1Data* data) {
|
||||
data->encoder_counter = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static LevelDuration protocol_1_encoder_yield(Protocol1Data* data) {
|
||||
data->encoder_counter++;
|
||||
return level_duration_make(!(data->encoder_counter % 2), 100);
|
||||
}
|
||||
|
||||
/*********************** PROTOCOLS DESCRIPTION ***********************/
|
||||
static const ProtocolBase protocol_0 = {
|
||||
.name = "Protocol 0",
|
||||
.manufacturer = "Manufacturer 0",
|
||||
.data_size = 4,
|
||||
.alloc = (ProtocolAlloc)protocol_0_alloc,
|
||||
.free = (ProtocolFree)protocol_0_free,
|
||||
.get_data = (ProtocolGetData)protocol_0_get_data,
|
||||
.decoder =
|
||||
{
|
||||
.start = (ProtocolDecoderStart)protocol_0_decoder_start,
|
||||
.feed = (ProtocolDecoderFeed)protocol_0_decoder_feed,
|
||||
},
|
||||
.encoder =
|
||||
{
|
||||
.start = (ProtocolEncoderStart)protocol_0_encoder_start,
|
||||
.yield = (ProtocolEncoderYield)protocol_0_encoder_yield,
|
||||
},
|
||||
};
|
||||
|
||||
static const ProtocolBase protocol_1 = {
|
||||
.name = "Protocol 1",
|
||||
.manufacturer = "Manufacturer 1",
|
||||
.data_size = 8,
|
||||
.alloc = (ProtocolAlloc)protocol_1_alloc,
|
||||
.free = (ProtocolFree)protocol_1_free,
|
||||
.get_data = (ProtocolGetData)protocol_1_get_data,
|
||||
.decoder =
|
||||
{
|
||||
.start = (ProtocolDecoderStart)protocol_1_decoder_start,
|
||||
.feed = (ProtocolDecoderFeed)protocol_1_decoder_feed,
|
||||
},
|
||||
.encoder =
|
||||
{
|
||||
.start = (ProtocolEncoderStart)protocol_1_encoder_start,
|
||||
.yield = (ProtocolEncoderYield)protocol_1_encoder_yield,
|
||||
},
|
||||
};
|
||||
|
||||
static const ProtocolBase* test_protocols_base[] = {
|
||||
[TestDictProtocol0] = &protocol_0,
|
||||
[TestDictProtocol1] = &protocol_1,
|
||||
};
|
||||
|
||||
MU_TEST(test_protocol_dict) {
|
||||
ProtocolDict* dict = protocol_dict_alloc(test_protocols_base, TestDictProtocolMax);
|
||||
size_t max_data_size = protocol_dict_get_max_data_size(dict);
|
||||
mu_assert_int_eq(8, max_data_size);
|
||||
uint8_t* data = malloc(max_data_size);
|
||||
|
||||
protocol_dict_decoders_start(dict);
|
||||
ProtocolId protocol_id = PROTOCOL_NO;
|
||||
|
||||
for(size_t i = 0; i < 100; i++) {
|
||||
protocol_id = protocol_dict_decoders_feed(dict, i % 2, 100);
|
||||
mu_assert_int_eq(PROTOCOL_NO, protocol_id);
|
||||
}
|
||||
|
||||
// trigger protocol 1
|
||||
protocol_id = protocol_dict_decoders_feed(dict, true, 543);
|
||||
mu_assert_int_eq(TestDictProtocol1, protocol_id);
|
||||
|
||||
mu_assert_string_eq("Protocol 1", protocol_dict_get_name(dict, protocol_id));
|
||||
mu_assert_string_eq("Manufacturer 1", protocol_dict_get_manufacturer(dict, protocol_id));
|
||||
|
||||
size_t data_size = protocol_dict_get_data_size(dict, protocol_id);
|
||||
mu_assert_int_eq(8, data_size);
|
||||
|
||||
protocol_dict_get_data(dict, protocol_id, data, data_size);
|
||||
mu_assert_mem_eq(&protocol_1_decoder_result, data, data_size);
|
||||
|
||||
// trigger protocol 0
|
||||
protocol_id = protocol_dict_decoders_feed(dict, true, 666);
|
||||
mu_assert_int_eq(TestDictProtocol0, protocol_id);
|
||||
|
||||
mu_assert_string_eq("Protocol 0", protocol_dict_get_name(dict, protocol_id));
|
||||
mu_assert_string_eq("Manufacturer 0", protocol_dict_get_manufacturer(dict, protocol_id));
|
||||
|
||||
data_size = protocol_dict_get_data_size(dict, protocol_id);
|
||||
mu_assert_int_eq(4, data_size);
|
||||
|
||||
protocol_dict_get_data(dict, protocol_id, data, data_size);
|
||||
mu_assert_mem_eq(&protocol_0_decoder_result, data, data_size);
|
||||
|
||||
protocol_dict_decoders_start(dict);
|
||||
|
||||
protocol_id = TestDictProtocol0;
|
||||
|
||||
const uint8_t protocol_0_test_data[4] = {100, 0, 0, 0};
|
||||
protocol_dict_set_data(dict, protocol_id, protocol_0_test_data, 4);
|
||||
|
||||
mu_check(protocol_dict_encoder_start(dict, protocol_id));
|
||||
|
||||
LevelDuration level;
|
||||
level = protocol_dict_encoder_yield(dict, protocol_id);
|
||||
mu_assert_int_eq(true, level_duration_get_level(level));
|
||||
mu_assert_int_eq(100, level_duration_get_duration(level));
|
||||
level = protocol_dict_encoder_yield(dict, protocol_id);
|
||||
mu_assert_int_eq(false, level_duration_get_level(level));
|
||||
mu_assert_int_eq(100, level_duration_get_duration(level));
|
||||
level = protocol_dict_encoder_yield(dict, protocol_id);
|
||||
mu_assert_int_eq(true, level_duration_get_level(level));
|
||||
mu_assert_int_eq(100, level_duration_get_duration(level));
|
||||
|
||||
mu_check(protocol_dict_encoder_start(dict, protocol_id));
|
||||
level = protocol_dict_encoder_yield(dict, protocol_id);
|
||||
mu_assert_int_eq(true, level_duration_get_level(level));
|
||||
mu_assert_int_eq(100, level_duration_get_duration(level));
|
||||
|
||||
protocol_dict_free(dict);
|
||||
free(data);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(test_protocol_dict_suite) {
|
||||
MU_RUN_TEST(test_protocol_dict);
|
||||
}
|
||||
|
||||
int run_minunit_test_protocol_dict() {
|
||||
MU_RUN_SUITE(test_protocol_dict_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
1803
applications/debug/unit_tests/rpc/rpc_test.c
Normal file
1803
applications/debug/unit_tests/rpc/rpc_test.c
Normal file
File diff suppressed because it is too large
Load Diff
272
applications/debug/unit_tests/storage/dirwalk_test.c
Normal file
272
applications/debug/unit_tests/storage/dirwalk_test.c
Normal file
@@ -0,0 +1,272 @@
|
||||
#include "../minunit.h"
|
||||
#include <furi.h>
|
||||
#include <m-dict.h>
|
||||
#include <toolbox/dir_walk.h>
|
||||
|
||||
static const char* const storage_test_dirwalk_paths[] = {
|
||||
"1",
|
||||
"11",
|
||||
"111",
|
||||
"1/2",
|
||||
"1/22",
|
||||
"1/222",
|
||||
"11/2",
|
||||
"111/2",
|
||||
"111/22",
|
||||
"111/22/33",
|
||||
};
|
||||
|
||||
static const char* const storage_test_dirwalk_files[] = {
|
||||
"file1.test",
|
||||
"file2.test",
|
||||
"file3.ext_test",
|
||||
"1/file1.test",
|
||||
"111/22/33/file1.test",
|
||||
"111/22/33/file2.test",
|
||||
"111/22/33/file3.ext_test",
|
||||
"111/22/33/file4.ext_test",
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char* const path;
|
||||
bool is_dir;
|
||||
} StorageTestPathDesc;
|
||||
|
||||
const StorageTestPathDesc storage_test_dirwalk_full[] = {
|
||||
{.path = "1", .is_dir = true},
|
||||
{.path = "11", .is_dir = true},
|
||||
{.path = "111", .is_dir = true},
|
||||
{.path = "1/2", .is_dir = true},
|
||||
{.path = "1/22", .is_dir = true},
|
||||
{.path = "1/222", .is_dir = true},
|
||||
{.path = "11/2", .is_dir = true},
|
||||
{.path = "111/2", .is_dir = true},
|
||||
{.path = "111/22", .is_dir = true},
|
||||
{.path = "111/22/33", .is_dir = true},
|
||||
{.path = "file1.test", .is_dir = false},
|
||||
{.path = "file2.test", .is_dir = false},
|
||||
{.path = "file3.ext_test", .is_dir = false},
|
||||
{.path = "1/file1.test", .is_dir = false},
|
||||
{.path = "111/22/33/file1.test", .is_dir = false},
|
||||
{.path = "111/22/33/file2.test", .is_dir = false},
|
||||
{.path = "111/22/33/file3.ext_test", .is_dir = false},
|
||||
{.path = "111/22/33/file4.ext_test", .is_dir = false},
|
||||
};
|
||||
|
||||
const StorageTestPathDesc storage_test_dirwalk_no_recursive[] = {
|
||||
{.path = "1", .is_dir = true},
|
||||
{.path = "11", .is_dir = true},
|
||||
{.path = "111", .is_dir = true},
|
||||
{.path = "file1.test", .is_dir = false},
|
||||
{.path = "file2.test", .is_dir = false},
|
||||
{.path = "file3.ext_test", .is_dir = false},
|
||||
};
|
||||
|
||||
const StorageTestPathDesc storage_test_dirwalk_filter[] = {
|
||||
{.path = "file1.test", .is_dir = false},
|
||||
{.path = "file2.test", .is_dir = false},
|
||||
{.path = "1/file1.test", .is_dir = false},
|
||||
{.path = "111/22/33/file1.test", .is_dir = false},
|
||||
{.path = "111/22/33/file2.test", .is_dir = false},
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
bool is_dir;
|
||||
bool visited;
|
||||
} StorageTestPath;
|
||||
|
||||
DICT_DEF2(StorageTestPathDict, string_t, STRING_OPLIST, StorageTestPath, M_POD_OPLIST)
|
||||
|
||||
static StorageTestPathDict_t*
|
||||
storage_test_paths_alloc(const StorageTestPathDesc paths[], size_t paths_count) {
|
||||
StorageTestPathDict_t* data = malloc(sizeof(StorageTestPathDict_t));
|
||||
StorageTestPathDict_init(*data);
|
||||
|
||||
for(size_t i = 0; i < paths_count; i++) {
|
||||
string_t key;
|
||||
string_init_set(key, paths[i].path);
|
||||
StorageTestPath value = {
|
||||
.is_dir = paths[i].is_dir,
|
||||
.visited = false,
|
||||
};
|
||||
|
||||
StorageTestPathDict_set_at(*data, key, value);
|
||||
string_clear(key);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void storage_test_paths_free(StorageTestPathDict_t* data) {
|
||||
StorageTestPathDict_clear(*data);
|
||||
free(data);
|
||||
}
|
||||
|
||||
static bool storage_test_paths_mark(StorageTestPathDict_t* data, string_t path, bool is_dir) {
|
||||
bool found = false;
|
||||
|
||||
StorageTestPath* record = StorageTestPathDict_get(*data, path);
|
||||
if(record) {
|
||||
if(is_dir == record->is_dir) {
|
||||
if(record->visited == false) {
|
||||
record->visited = true;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static bool storage_test_paths_check(StorageTestPathDict_t* data) {
|
||||
bool error = false;
|
||||
|
||||
StorageTestPathDict_it_t it;
|
||||
for(StorageTestPathDict_it(it, *data); !StorageTestPathDict_end_p(it);
|
||||
StorageTestPathDict_next(it)) {
|
||||
const StorageTestPathDict_itref_t* itref = StorageTestPathDict_cref(it);
|
||||
|
||||
if(itref->value.visited == false) {
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static bool write_file_13DA(Storage* storage, const char* path) {
|
||||
File* file = storage_file_alloc(storage);
|
||||
bool result = false;
|
||||
if(storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
|
||||
result = storage_file_write(file, "13DA", 4) == 4;
|
||||
}
|
||||
storage_file_close(file);
|
||||
storage_file_free(file);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void storage_dirs_create(Storage* storage, const char* base) {
|
||||
string_t path;
|
||||
string_init(path);
|
||||
|
||||
storage_common_mkdir(storage, base);
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(storage_test_dirwalk_paths); i++) {
|
||||
string_printf(path, "%s/%s", base, storage_test_dirwalk_paths[i]);
|
||||
storage_common_mkdir(storage, string_get_cstr(path));
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(storage_test_dirwalk_files); i++) {
|
||||
string_printf(path, "%s/%s", base, storage_test_dirwalk_files[i]);
|
||||
write_file_13DA(storage, string_get_cstr(path));
|
||||
}
|
||||
|
||||
string_clear(path);
|
||||
}
|
||||
|
||||
MU_TEST_1(test_dirwalk_full, Storage* storage) {
|
||||
string_t path;
|
||||
string_init(path);
|
||||
FileInfo fileinfo;
|
||||
|
||||
StorageTestPathDict_t* paths =
|
||||
storage_test_paths_alloc(storage_test_dirwalk_full, COUNT_OF(storage_test_dirwalk_full));
|
||||
|
||||
DirWalk* dir_walk = dir_walk_alloc(storage);
|
||||
mu_check(dir_walk_open(dir_walk, EXT_PATH("dirwalk")));
|
||||
|
||||
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
|
||||
string_right(path, strlen(EXT_PATH("dirwalk/")));
|
||||
mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
|
||||
}
|
||||
|
||||
dir_walk_free(dir_walk);
|
||||
string_clear(path);
|
||||
|
||||
mu_check(storage_test_paths_check(paths) == false);
|
||||
|
||||
storage_test_paths_free(paths);
|
||||
}
|
||||
|
||||
MU_TEST_1(test_dirwalk_no_recursive, Storage* storage) {
|
||||
string_t path;
|
||||
string_init(path);
|
||||
FileInfo fileinfo;
|
||||
|
||||
StorageTestPathDict_t* paths = storage_test_paths_alloc(
|
||||
storage_test_dirwalk_no_recursive, COUNT_OF(storage_test_dirwalk_no_recursive));
|
||||
|
||||
DirWalk* dir_walk = dir_walk_alloc(storage);
|
||||
dir_walk_set_recursive(dir_walk, false);
|
||||
mu_check(dir_walk_open(dir_walk, EXT_PATH("dirwalk")));
|
||||
|
||||
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
|
||||
string_right(path, strlen(EXT_PATH("dirwalk/")));
|
||||
mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
|
||||
}
|
||||
|
||||
dir_walk_free(dir_walk);
|
||||
string_clear(path);
|
||||
|
||||
mu_check(storage_test_paths_check(paths) == false);
|
||||
|
||||
storage_test_paths_free(paths);
|
||||
}
|
||||
|
||||
static bool test_dirwalk_filter_no_folder_ext(const char* name, FileInfo* fileinfo, void* ctx) {
|
||||
UNUSED(ctx);
|
||||
|
||||
// only files
|
||||
if(!(fileinfo->flags & FSF_DIRECTORY)) {
|
||||
// with ".test" in name
|
||||
if(strstr(name, ".test") != NULL) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
MU_TEST_1(test_dirwalk_filter, Storage* storage) {
|
||||
string_t path;
|
||||
string_init(path);
|
||||
FileInfo fileinfo;
|
||||
|
||||
StorageTestPathDict_t* paths = storage_test_paths_alloc(
|
||||
storage_test_dirwalk_filter, COUNT_OF(storage_test_dirwalk_filter));
|
||||
|
||||
DirWalk* dir_walk = dir_walk_alloc(storage);
|
||||
dir_walk_set_filter_cb(dir_walk, test_dirwalk_filter_no_folder_ext, NULL);
|
||||
mu_check(dir_walk_open(dir_walk, EXT_PATH("dirwalk")));
|
||||
|
||||
while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) {
|
||||
string_right(path, strlen(EXT_PATH("dirwalk/")));
|
||||
mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY)));
|
||||
}
|
||||
|
||||
dir_walk_free(dir_walk);
|
||||
string_clear(path);
|
||||
|
||||
mu_check(storage_test_paths_check(paths) == false);
|
||||
|
||||
storage_test_paths_free(paths);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(test_dirwalk_suite) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
storage_dirs_create(storage, EXT_PATH("dirwalk"));
|
||||
|
||||
MU_RUN_TEST_1(test_dirwalk_full, storage);
|
||||
MU_RUN_TEST_1(test_dirwalk_no_recursive, storage);
|
||||
MU_RUN_TEST_1(test_dirwalk_filter, storage);
|
||||
|
||||
storage_simply_remove_recursive(storage, EXT_PATH("dirwalk"));
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
int run_minunit_test_dirwalk() {
|
||||
MU_RUN_SUITE(test_dirwalk_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
317
applications/debug/unit_tests/storage/storage_test.c
Normal file
317
applications/debug/unit_tests/storage/storage_test.c
Normal file
@@ -0,0 +1,317 @@
|
||||
#include "../minunit.h"
|
||||
#include <furi.h>
|
||||
#include <storage/storage.h>
|
||||
|
||||
#define STORAGE_LOCKED_FILE EXT_PATH("locked_file.test")
|
||||
#define STORAGE_LOCKED_DIR STORAGE_INT_PATH_PREFIX
|
||||
|
||||
static void storage_file_open_lock_setup() {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(storage);
|
||||
storage_simply_remove(storage, STORAGE_LOCKED_FILE);
|
||||
mu_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_WRITE, FSOM_CREATE_NEW));
|
||||
mu_check(storage_file_write(file, "0123", 4) == 4);
|
||||
mu_check(storage_file_close(file));
|
||||
storage_file_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static void storage_file_open_lock_teardown() {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
mu_check(storage_simply_remove(storage, STORAGE_LOCKED_FILE));
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static int32_t storage_file_locker(void* ctx) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FuriSemaphore* semaphore = ctx;
|
||||
File* file = storage_file_alloc(storage);
|
||||
furi_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING));
|
||||
furi_semaphore_release(semaphore);
|
||||
furi_delay_ms(1000);
|
||||
|
||||
furi_check(storage_file_close(file));
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
storage_file_free(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
MU_TEST(storage_file_open_lock) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
bool result = false;
|
||||
FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0);
|
||||
File* file = storage_file_alloc(storage);
|
||||
|
||||
// file_locker thread start
|
||||
FuriThread* locker_thread = furi_thread_alloc();
|
||||
furi_thread_set_name(locker_thread, "StorageFileLocker");
|
||||
furi_thread_set_stack_size(locker_thread, 2048);
|
||||
furi_thread_set_context(locker_thread, semaphore);
|
||||
furi_thread_set_callback(locker_thread, storage_file_locker);
|
||||
furi_thread_start(locker_thread);
|
||||
|
||||
// wait for file lock
|
||||
furi_semaphore_acquire(semaphore, FuriWaitForever);
|
||||
furi_semaphore_free(semaphore);
|
||||
|
||||
result = storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING);
|
||||
storage_file_close(file);
|
||||
|
||||
// file_locker thread stop
|
||||
mu_check(furi_thread_join(locker_thread) == FuriStatusOk);
|
||||
furi_thread_free(locker_thread);
|
||||
|
||||
// clean data
|
||||
storage_file_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
mu_assert(result, "cannot open locked file");
|
||||
}
|
||||
|
||||
MU_TEST(storage_file_open_close) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
File* file;
|
||||
|
||||
file = storage_file_alloc(storage);
|
||||
mu_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING));
|
||||
storage_file_close(file);
|
||||
storage_file_free(file);
|
||||
|
||||
for(size_t i = 0; i < 10; i++) {
|
||||
file = storage_file_alloc(storage);
|
||||
mu_check(
|
||||
storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING));
|
||||
storage_file_free(file);
|
||||
}
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(storage_file) {
|
||||
storage_file_open_lock_setup();
|
||||
MU_RUN_TEST(storage_file_open_close);
|
||||
MU_RUN_TEST(storage_file_open_lock);
|
||||
storage_file_open_lock_teardown();
|
||||
}
|
||||
|
||||
MU_TEST(storage_dir_open_close) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
File* file;
|
||||
|
||||
file = storage_file_alloc(storage);
|
||||
mu_check(storage_dir_open(file, STORAGE_LOCKED_DIR));
|
||||
storage_dir_close(file);
|
||||
storage_file_free(file);
|
||||
|
||||
for(size_t i = 0; i < 10; i++) {
|
||||
file = storage_file_alloc(storage);
|
||||
mu_check(storage_dir_open(file, STORAGE_LOCKED_DIR));
|
||||
storage_file_free(file);
|
||||
}
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
static int32_t storage_dir_locker(void* ctx) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FuriSemaphore* semaphore = ctx;
|
||||
File* file = storage_file_alloc(storage);
|
||||
furi_check(storage_dir_open(file, STORAGE_LOCKED_DIR));
|
||||
furi_semaphore_release(semaphore);
|
||||
furi_delay_ms(1000);
|
||||
|
||||
furi_check(storage_dir_close(file));
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
storage_file_free(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
MU_TEST(storage_dir_open_lock) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
bool result = false;
|
||||
FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0);
|
||||
File* file = storage_file_alloc(storage);
|
||||
|
||||
// file_locker thread start
|
||||
FuriThread* locker_thread = furi_thread_alloc();
|
||||
furi_thread_set_name(locker_thread, "StorageDirLocker");
|
||||
furi_thread_set_stack_size(locker_thread, 2048);
|
||||
furi_thread_set_context(locker_thread, semaphore);
|
||||
furi_thread_set_callback(locker_thread, storage_dir_locker);
|
||||
furi_thread_start(locker_thread);
|
||||
|
||||
// wait for dir lock
|
||||
furi_semaphore_acquire(semaphore, FuriWaitForever);
|
||||
furi_semaphore_free(semaphore);
|
||||
|
||||
result = storage_dir_open(file, STORAGE_LOCKED_DIR);
|
||||
storage_dir_close(file);
|
||||
|
||||
// file_locker thread stop
|
||||
mu_check(furi_thread_join(locker_thread) == FuriStatusOk);
|
||||
furi_thread_free(locker_thread);
|
||||
|
||||
// clean data
|
||||
storage_file_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
mu_assert(result, "cannot open locked dir");
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(storage_dir) {
|
||||
MU_RUN_TEST(storage_dir_open_close);
|
||||
MU_RUN_TEST(storage_dir_open_lock);
|
||||
}
|
||||
|
||||
static const char* const storage_copy_test_paths[] = {
|
||||
"1",
|
||||
"11",
|
||||
"111",
|
||||
"1/2",
|
||||
"1/22",
|
||||
"1/222",
|
||||
"11/1",
|
||||
"111/2",
|
||||
"111/22",
|
||||
"111/22/33",
|
||||
};
|
||||
|
||||
static const char* const storage_copy_test_files[] = {
|
||||
"file.test",
|
||||
"1/file.test",
|
||||
"111/22/33/file.test",
|
||||
};
|
||||
|
||||
static bool write_file_13DA(Storage* storage, const char* path) {
|
||||
File* file = storage_file_alloc(storage);
|
||||
bool result = false;
|
||||
if(storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
|
||||
result = storage_file_write(file, "13DA", 4) == 4;
|
||||
}
|
||||
storage_file_close(file);
|
||||
storage_file_free(file);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool check_file_13DA(Storage* storage, const char* path) {
|
||||
File* file = storage_file_alloc(storage);
|
||||
bool result = false;
|
||||
if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
char data[10] = {0};
|
||||
result = storage_file_read(file, data, 4) == 4;
|
||||
if(result) {
|
||||
result = memcmp(data, "13DA", 4) == 0;
|
||||
}
|
||||
}
|
||||
storage_file_close(file);
|
||||
storage_file_free(file);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void storage_dir_create(Storage* storage, const char* base) {
|
||||
string_t path;
|
||||
string_init(path);
|
||||
|
||||
storage_common_mkdir(storage, base);
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(storage_copy_test_paths); i++) {
|
||||
string_printf(path, "%s/%s", base, storage_copy_test_paths[i]);
|
||||
storage_common_mkdir(storage, string_get_cstr(path));
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(storage_copy_test_files); i++) {
|
||||
string_printf(path, "%s/%s", base, storage_copy_test_files[i]);
|
||||
write_file_13DA(storage, string_get_cstr(path));
|
||||
}
|
||||
|
||||
string_clear(path);
|
||||
}
|
||||
|
||||
static void storage_dir_remove(Storage* storage, const char* base) {
|
||||
storage_simply_remove_recursive(storage, base);
|
||||
}
|
||||
|
||||
static bool storage_dir_rename_check(Storage* storage, const char* base) {
|
||||
bool result = false;
|
||||
string_t path;
|
||||
string_init(path);
|
||||
|
||||
result = (storage_common_stat(storage, base, NULL) == FSE_OK);
|
||||
|
||||
if(result) {
|
||||
for(size_t i = 0; i < COUNT_OF(storage_copy_test_paths); i++) {
|
||||
string_printf(path, "%s/%s", base, storage_copy_test_paths[i]);
|
||||
result = (storage_common_stat(storage, string_get_cstr(path), NULL) == FSE_OK);
|
||||
if(!result) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(result) {
|
||||
for(size_t i = 0; i < COUNT_OF(storage_copy_test_files); i++) {
|
||||
string_printf(path, "%s/%s", base, storage_copy_test_files[i]);
|
||||
result = check_file_13DA(storage, string_get_cstr(path));
|
||||
if(!result) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string_clear(path);
|
||||
return result;
|
||||
}
|
||||
|
||||
MU_TEST(storage_file_rename) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
File* file = storage_file_alloc(storage);
|
||||
|
||||
mu_check(write_file_13DA(storage, EXT_PATH("file.old")));
|
||||
mu_check(check_file_13DA(storage, EXT_PATH("file.old")));
|
||||
mu_assert_int_eq(
|
||||
FSE_OK, storage_common_rename(storage, EXT_PATH("file.old"), EXT_PATH("file.new")));
|
||||
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("file.old"), NULL));
|
||||
mu_assert_int_eq(FSE_OK, storage_common_stat(storage, EXT_PATH("file.new"), NULL));
|
||||
mu_check(check_file_13DA(storage, EXT_PATH("file.new")));
|
||||
mu_assert_int_eq(FSE_OK, storage_common_remove(storage, EXT_PATH("file.new")));
|
||||
|
||||
storage_file_free(file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
MU_TEST(storage_dir_rename) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
storage_dir_create(storage, EXT_PATH("dir.old"));
|
||||
|
||||
mu_check(storage_dir_rename_check(storage, EXT_PATH("dir.old")));
|
||||
|
||||
mu_assert_int_eq(
|
||||
FSE_OK, storage_common_rename(storage, EXT_PATH("dir.old"), EXT_PATH("dir.new")));
|
||||
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("dir.old"), NULL));
|
||||
mu_check(storage_dir_rename_check(storage, EXT_PATH("dir.new")));
|
||||
|
||||
storage_dir_remove(storage, EXT_PATH("dir.new"));
|
||||
mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("dir.new"), NULL));
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(storage_rename) {
|
||||
MU_RUN_TEST(storage_file_rename);
|
||||
MU_RUN_TEST(storage_dir_rename);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
storage_dir_remove(storage, EXT_PATH("dir.old"));
|
||||
storage_dir_remove(storage, EXT_PATH("dir.new"));
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
int run_minunit_test_storage() {
|
||||
MU_RUN_SUITE(storage_file);
|
||||
MU_RUN_SUITE(storage_dir);
|
||||
MU_RUN_SUITE(storage_rename);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
508
applications/debug/unit_tests/stream/stream_test.c
Normal file
508
applications/debug/unit_tests/stream/stream_test.c
Normal file
@@ -0,0 +1,508 @@
|
||||
#include <furi.h>
|
||||
#include <toolbox/stream/stream.h>
|
||||
#include <toolbox/stream/string_stream.h>
|
||||
#include <toolbox/stream/file_stream.h>
|
||||
#include <toolbox/stream/buffered_file_stream.h>
|
||||
#include <storage/storage.h>
|
||||
#include "../minunit.h"
|
||||
|
||||
static const char* stream_test_data = "I write differently from what I speak, "
|
||||
"I speak differently from what I think, "
|
||||
"I think differently from the way I ought to think, "
|
||||
"and so it all proceeds into deepest darkness.";
|
||||
|
||||
static const char* stream_test_left_data = "There are two cardinal human sins ";
|
||||
static const char* stream_test_right_data =
|
||||
"from which all others derive: impatience and indolence.";
|
||||
|
||||
MU_TEST_1(stream_composite_subtest, Stream* stream) {
|
||||
const size_t data_size = 128;
|
||||
uint8_t data[data_size];
|
||||
string_t string_lee;
|
||||
string_init_set(string_lee, "lee");
|
||||
|
||||
// test that stream is empty
|
||||
// "" -> ""
|
||||
mu_check(stream_size(stream) == 0);
|
||||
mu_check(stream_eof(stream));
|
||||
mu_check(stream_tell(stream) == 0);
|
||||
mu_check(stream_read(stream, data, data_size) == 0);
|
||||
mu_check(stream_eof(stream));
|
||||
mu_check(stream_tell(stream) == 0);
|
||||
|
||||
// write char
|
||||
// "" -> "2"
|
||||
mu_check(stream_write_char(stream, '2') == 1);
|
||||
mu_check(stream_size(stream) == 1);
|
||||
mu_check(stream_tell(stream) == 1);
|
||||
mu_check(stream_eof(stream));
|
||||
|
||||
// test rewind and eof
|
||||
stream_rewind(stream);
|
||||
mu_check(stream_size(stream) == 1);
|
||||
mu_check(stream_tell(stream) == 0);
|
||||
mu_check(!stream_eof(stream));
|
||||
|
||||
// add another char with replacement
|
||||
// "2" -> "1"
|
||||
mu_check(stream_write_char(stream, '1') == 1);
|
||||
mu_check(stream_size(stream) == 1);
|
||||
mu_check(stream_tell(stream) == 1);
|
||||
mu_check(stream_eof(stream));
|
||||
|
||||
// write string
|
||||
// "1" -> "1337_69"
|
||||
mu_check(stream_write_cstring(stream, "337_69") == 6);
|
||||
mu_check(stream_size(stream) == 7);
|
||||
mu_check(stream_tell(stream) == 7);
|
||||
mu_check(stream_eof(stream));
|
||||
|
||||
// read data
|
||||
memset(data, 0, data_size);
|
||||
stream_rewind(stream);
|
||||
mu_check(stream_read(stream, data, data_size) == 7);
|
||||
mu_check(strcmp((char*)data, "1337_69") == 0);
|
||||
|
||||
// test misc seeks
|
||||
mu_check(stream_seek(stream, 2, StreamOffsetFromStart));
|
||||
mu_check(stream_tell(stream) == 2);
|
||||
mu_check(!stream_seek(stream, 9000, StreamOffsetFromStart));
|
||||
mu_check(stream_tell(stream) == 7);
|
||||
mu_check(stream_eof(stream));
|
||||
mu_check(stream_seek(stream, -3, StreamOffsetFromEnd));
|
||||
mu_check(stream_tell(stream) == 4);
|
||||
|
||||
// write string with replacemet
|
||||
// "1337_69" -> "1337lee"
|
||||
mu_check(stream_write_string(stream, string_lee) == 3);
|
||||
mu_check(stream_size(stream) == 7);
|
||||
mu_check(stream_tell(stream) == 7);
|
||||
mu_check(stream_eof(stream));
|
||||
|
||||
// append char
|
||||
// "1337lee" -> "1337leet"
|
||||
mu_check(stream_write(stream, (uint8_t*)"t", 1) == 1);
|
||||
mu_check(stream_size(stream) == 8);
|
||||
mu_check(stream_tell(stream) == 8);
|
||||
mu_check(stream_eof(stream));
|
||||
|
||||
// read data
|
||||
memset(data, 0, data_size);
|
||||
stream_rewind(stream);
|
||||
mu_check(stream_read(stream, data, data_size) == 8);
|
||||
mu_check(strcmp((char*)data, "1337leet") == 0);
|
||||
mu_check(stream_tell(stream) == 8);
|
||||
mu_check(stream_eof(stream));
|
||||
|
||||
// negative seek from current position -> clamp to 0
|
||||
mu_check(!stream_seek(stream, -9000, StreamOffsetFromCurrent));
|
||||
mu_check(stream_tell(stream) == 0);
|
||||
|
||||
// negative seek from start position -> clamp to 0
|
||||
stream_rewind(stream);
|
||||
mu_check(!stream_seek(stream, -3, StreamOffsetFromStart));
|
||||
mu_check(stream_tell(stream) == 0);
|
||||
|
||||
// zero seek from current position -> clamp to stream size
|
||||
mu_check(stream_seek(stream, 0, StreamOffsetFromEnd));
|
||||
mu_check(stream_tell(stream) == 8);
|
||||
|
||||
// negative seek from end position -> clamp to 0
|
||||
mu_check(!stream_seek(stream, -9000, StreamOffsetFromEnd));
|
||||
mu_check(stream_tell(stream) == 0);
|
||||
|
||||
// clean stream
|
||||
stream_clean(stream);
|
||||
mu_check(stream_size(stream) == 0);
|
||||
mu_check(stream_eof(stream));
|
||||
mu_check(stream_tell(stream) == 0);
|
||||
|
||||
// write format
|
||||
// "" -> "dio666"
|
||||
mu_check(stream_write_format(stream, "%s%d", "dio", 666) == 6);
|
||||
mu_check(stream_size(stream) == 6);
|
||||
mu_check(stream_eof(stream));
|
||||
mu_check(stream_tell(stream) == 6);
|
||||
|
||||
// read data
|
||||
memset(data, 0, data_size);
|
||||
stream_rewind(stream);
|
||||
mu_check(stream_read(stream, data, data_size) == 6);
|
||||
mu_check(strcmp((char*)data, "dio666") == 0);
|
||||
|
||||
// clean and write cstring
|
||||
// "dio666" -> "" -> "1234567890"
|
||||
stream_clean(stream);
|
||||
mu_check(stream_write_cstring(stream, "1234567890") == 10);
|
||||
|
||||
// delete 4 bytes from 1 pos
|
||||
// "1xxxx67890" -> "167890"
|
||||
mu_check(stream_seek(stream, 1, StreamOffsetFromStart));
|
||||
mu_check(stream_delete(stream, 4));
|
||||
mu_assert_int_eq(6, stream_size(stream));
|
||||
|
||||
// read data
|
||||
memset(data, 0, data_size);
|
||||
stream_rewind(stream);
|
||||
mu_assert_int_eq(6, stream_read(stream, data, data_size));
|
||||
mu_check(strcmp((char*)data, "167890") == 0);
|
||||
|
||||
// write cstring
|
||||
// "167890" -> "167890It Was Me, Dio!"
|
||||
mu_check(stream_write_cstring(stream, "It Was Me, Dio!") == 15);
|
||||
|
||||
// delete 1337 bytes from 1 pos
|
||||
// and check that we can delete only 20 bytes
|
||||
// "1xxxxxxxxxxxxxxxxxxxx" -> "1"
|
||||
mu_check(stream_seek(stream, 1, StreamOffsetFromStart));
|
||||
mu_check(stream_delete(stream, 1337));
|
||||
mu_assert_int_eq(1, stream_size(stream));
|
||||
|
||||
// read data
|
||||
memset(data, 0, data_size);
|
||||
stream_rewind(stream);
|
||||
mu_check(stream_read(stream, data, data_size) == 1);
|
||||
mu_check(strcmp((char*)data, "1") == 0);
|
||||
|
||||
// write cstring from 0 pos, replacing 1 byte
|
||||
// "1" -> "Oh? You're roaching me?"
|
||||
mu_check(stream_rewind(stream));
|
||||
mu_assert_int_eq(23, stream_write_cstring(stream, "Oh? You're roaching me?"));
|
||||
|
||||
// insert 11 bytes to 0 pos
|
||||
// "Oh? You're roaching me?" -> "Za Warudo! Oh? You're roaching me?"
|
||||
mu_check(stream_rewind(stream));
|
||||
mu_check(stream_insert(stream, (uint8_t*)"Za Warudo! ", 11));
|
||||
mu_assert_int_eq(34, stream_size(stream));
|
||||
|
||||
// read data
|
||||
memset(data, 0, data_size);
|
||||
stream_rewind(stream);
|
||||
mu_assert_int_eq(34, stream_read(stream, data, data_size));
|
||||
mu_assert_string_eq("Za Warudo! Oh? You're roaching me?", (char*)data);
|
||||
|
||||
// insert cstring to 22 pos
|
||||
// "Za Warudo! Oh? You're roaching me?" -> "Za Warudo! Oh? You're approaching me?"
|
||||
mu_check(stream_seek(stream, 22, StreamOffsetFromStart));
|
||||
mu_check(stream_insert_cstring(stream, "app"));
|
||||
mu_assert_int_eq(37, stream_size(stream));
|
||||
|
||||
// read data
|
||||
memset(data, 0, data_size);
|
||||
stream_rewind(stream);
|
||||
mu_assert_int_eq(37, stream_read(stream, data, data_size));
|
||||
mu_assert_string_eq("Za Warudo! Oh? You're approaching me?", (char*)data);
|
||||
|
||||
// insert cstring to the end of the stream
|
||||
// "Za Warudo! Oh? You're approaching me?" -> "Za Warudo! Oh? You're approaching me? It was me, Dio!"
|
||||
mu_check(stream_seek(stream, 0, StreamOffsetFromEnd));
|
||||
mu_check(stream_insert_cstring(stream, " It was me, Dio!"));
|
||||
mu_assert_int_eq(53, stream_size(stream));
|
||||
|
||||
// read data
|
||||
memset(data, 0, data_size);
|
||||
stream_rewind(stream);
|
||||
mu_assert_int_eq(53, stream_read(stream, data, data_size));
|
||||
mu_assert_string_eq("Za Warudo! Oh? You're approaching me? It was me, Dio!", (char*)data);
|
||||
|
||||
// delete 168430090 bytes from stream
|
||||
// and test that we can delete only 53
|
||||
mu_check(stream_rewind(stream));
|
||||
mu_check(stream_delete(stream, 0x0A0A0A0A));
|
||||
mu_assert_int_eq(0, stream_size(stream));
|
||||
mu_check(stream_eof(stream));
|
||||
mu_assert_int_eq(0, stream_tell(stream));
|
||||
|
||||
// clean stream
|
||||
stream_clean(stream);
|
||||
mu_assert_int_eq(0, stream_size(stream));
|
||||
mu_check(stream_eof(stream));
|
||||
mu_assert_int_eq(0, stream_tell(stream));
|
||||
|
||||
// insert formated string at the end of stream
|
||||
// "" -> "dio666"
|
||||
mu_check(stream_insert_format(stream, "%s%d", "dio", 666));
|
||||
mu_assert_int_eq(6, stream_size(stream));
|
||||
mu_check(stream_eof(stream));
|
||||
mu_assert_int_eq(6, stream_tell(stream));
|
||||
|
||||
// insert formated string at the end of stream
|
||||
// "dio666" -> "dio666zlo555"
|
||||
mu_check(stream_insert_format(stream, "%s%d", "zlo", 555));
|
||||
mu_assert_int_eq(12, stream_size(stream));
|
||||
mu_check(stream_eof(stream));
|
||||
mu_assert_int_eq(12, stream_tell(stream));
|
||||
|
||||
// insert formated string at the 6 pos
|
||||
// "dio666" -> "dio666baba13zlo555"
|
||||
mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
|
||||
mu_check(stream_insert_format(stream, "%s%d", "baba", 13));
|
||||
mu_assert_int_eq(18, stream_size(stream));
|
||||
mu_assert_int_eq(12, stream_tell(stream));
|
||||
|
||||
// read data
|
||||
memset(data, 0, data_size);
|
||||
stream_rewind(stream);
|
||||
mu_assert_int_eq(18, stream_read(stream, data, data_size));
|
||||
mu_assert_string_eq("dio666baba13zlo555", (char*)data);
|
||||
|
||||
// delete 6 chars from pos 6 and insert 1 chars
|
||||
// "dio666baba13zlo555" -> "dio666xzlo555"
|
||||
mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
|
||||
mu_check(stream_delete_and_insert_char(stream, 6, 'x'));
|
||||
mu_assert_int_eq(13, stream_size(stream));
|
||||
mu_assert_int_eq(7, stream_tell(stream));
|
||||
|
||||
// read data
|
||||
memset(data, 0, data_size);
|
||||
stream_rewind(stream);
|
||||
mu_check(stream_read(stream, data, data_size) == 13);
|
||||
mu_assert_string_eq("dio666xzlo555", (char*)data);
|
||||
|
||||
// delete 9000 chars from pos 6 and insert 3 chars from string
|
||||
// "dio666xzlo555" -> "dio666777"
|
||||
mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
|
||||
mu_check(stream_delete_and_insert_cstring(stream, 9000, "777"));
|
||||
mu_assert_int_eq(9, stream_size(stream));
|
||||
mu_assert_int_eq(9, stream_tell(stream));
|
||||
mu_check(stream_eof(stream));
|
||||
|
||||
string_clear(string_lee);
|
||||
}
|
||||
|
||||
MU_TEST(stream_composite_test) {
|
||||
// test string stream
|
||||
Stream* stream;
|
||||
stream = string_stream_alloc();
|
||||
MU_RUN_TEST_1(stream_composite_subtest, stream);
|
||||
stream_free(stream);
|
||||
|
||||
// test file stream
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
stream = file_stream_alloc(storage);
|
||||
mu_check(
|
||||
file_stream_open(stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
|
||||
MU_RUN_TEST_1(stream_composite_subtest, stream);
|
||||
stream_free(stream);
|
||||
|
||||
// test buffered file stream
|
||||
stream = buffered_file_stream_alloc(storage);
|
||||
mu_check(buffered_file_stream_open(
|
||||
stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
|
||||
MU_RUN_TEST_1(stream_composite_subtest, stream);
|
||||
stream_free(stream);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
MU_TEST_1(stream_write_subtest, Stream* stream) {
|
||||
mu_assert_int_eq(strlen(stream_test_data), stream_write_cstring(stream, stream_test_data));
|
||||
}
|
||||
|
||||
MU_TEST_1(stream_read_subtest, Stream* stream) {
|
||||
uint8_t data[256] = {0};
|
||||
mu_check(stream_rewind(stream));
|
||||
mu_assert_int_eq(strlen(stream_test_data), stream_read(stream, data, 256));
|
||||
mu_assert_string_eq(stream_test_data, (const char*)data);
|
||||
}
|
||||
|
||||
MU_TEST(stream_write_read_save_load_test) {
|
||||
Stream* stream_orig = string_stream_alloc();
|
||||
Stream* stream_copy = string_stream_alloc();
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
// write, read
|
||||
MU_RUN_TEST_1(stream_write_subtest, stream_orig);
|
||||
MU_RUN_TEST_1(stream_read_subtest, stream_orig);
|
||||
|
||||
// copy, read
|
||||
mu_assert_int_eq(strlen(stream_test_data), stream_copy_full(stream_orig, stream_copy));
|
||||
MU_RUN_TEST_1(stream_read_subtest, stream_orig);
|
||||
|
||||
// save to file
|
||||
mu_check(stream_seek(stream_orig, 0, StreamOffsetFromStart));
|
||||
mu_assert_int_eq(
|
||||
strlen(stream_test_data),
|
||||
stream_save_to_file(stream_orig, storage, EXT_PATH("filestream.str"), FSOM_CREATE_ALWAYS));
|
||||
|
||||
stream_free(stream_copy);
|
||||
stream_free(stream_orig);
|
||||
|
||||
// load from file, read
|
||||
Stream* stream_new = string_stream_alloc();
|
||||
mu_assert_int_eq(
|
||||
strlen(stream_test_data),
|
||||
stream_load_from_file(stream_new, storage, EXT_PATH("filestream.str")));
|
||||
MU_RUN_TEST_1(stream_read_subtest, stream_new);
|
||||
stream_free(stream_new);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
MU_TEST_1(stream_split_subtest, Stream* stream) {
|
||||
stream_clean(stream);
|
||||
stream_write_cstring(stream, stream_test_left_data);
|
||||
stream_write_cstring(stream, stream_test_right_data);
|
||||
|
||||
Stream* stream_left = string_stream_alloc();
|
||||
Stream* stream_right = string_stream_alloc();
|
||||
|
||||
mu_check(stream_seek(stream, strlen(stream_test_left_data), StreamOffsetFromStart));
|
||||
mu_check(stream_split(stream, stream_left, stream_right));
|
||||
|
||||
uint8_t data[256] = {0};
|
||||
mu_check(stream_rewind(stream_left));
|
||||
mu_assert_int_eq(strlen(stream_test_left_data), stream_read(stream_left, data, 256));
|
||||
mu_assert_string_eq(stream_test_left_data, (const char*)data);
|
||||
|
||||
mu_check(stream_rewind(stream_right));
|
||||
mu_assert_int_eq(strlen(stream_test_right_data), stream_read(stream_right, data, 256));
|
||||
mu_assert_string_eq(stream_test_right_data, (const char*)data);
|
||||
|
||||
stream_free(stream_right);
|
||||
stream_free(stream_left);
|
||||
}
|
||||
|
||||
MU_TEST(stream_split_test) {
|
||||
// test string stream
|
||||
Stream* stream;
|
||||
stream = string_stream_alloc();
|
||||
MU_RUN_TEST_1(stream_split_subtest, stream);
|
||||
stream_free(stream);
|
||||
|
||||
// test file stream
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
stream = file_stream_alloc(storage);
|
||||
mu_check(
|
||||
file_stream_open(stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
|
||||
MU_RUN_TEST_1(stream_split_subtest, stream);
|
||||
stream_free(stream);
|
||||
|
||||
// test buffered stream
|
||||
stream = buffered_file_stream_alloc(storage);
|
||||
mu_check(buffered_file_stream_open(
|
||||
stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
|
||||
MU_RUN_TEST_1(stream_split_subtest, stream);
|
||||
stream_free(stream);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
MU_TEST(stream_buffered_write_after_read_test) {
|
||||
const char* prefix = "I write ";
|
||||
const char* substr = "Hello there";
|
||||
|
||||
const size_t substr_len = strlen(substr);
|
||||
const size_t prefix_len = strlen(prefix);
|
||||
const size_t buf_size = substr_len + 1;
|
||||
|
||||
char buf[buf_size];
|
||||
memset(buf, 0, buf_size);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
Stream* stream = buffered_file_stream_alloc(storage);
|
||||
mu_check(buffered_file_stream_open(
|
||||
stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
|
||||
mu_assert_int_eq(strlen(stream_test_data), stream_write_cstring(stream, stream_test_data));
|
||||
mu_check(stream_rewind(stream));
|
||||
mu_assert_int_eq(prefix_len, stream_read(stream, (uint8_t*)buf, prefix_len));
|
||||
mu_assert_string_eq(prefix, buf);
|
||||
mu_assert_int_eq(substr_len, stream_write(stream, (uint8_t*)substr, substr_len));
|
||||
mu_check(stream_seek(stream, prefix_len, StreamOffsetFromStart));
|
||||
mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
|
||||
mu_assert_string_eq(substr, buf);
|
||||
|
||||
stream_free(stream);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
MU_TEST(stream_buffered_large_file_test) {
|
||||
string_t input_data;
|
||||
string_t output_data;
|
||||
string_init(input_data);
|
||||
string_init(output_data);
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
|
||||
// generate test data consisting of several identical lines
|
||||
const size_t data_size = 4096;
|
||||
const size_t line_size = strlen(stream_test_data);
|
||||
const size_t rep_count = data_size / line_size + 1;
|
||||
|
||||
for(size_t i = 0; i < rep_count; ++i) {
|
||||
string_cat_printf(input_data, "%s\n", stream_test_data);
|
||||
}
|
||||
|
||||
// write test data to file
|
||||
Stream* stream = buffered_file_stream_alloc(storage);
|
||||
mu_check(buffered_file_stream_open(
|
||||
stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
|
||||
mu_assert_int_eq(0, stream_size(stream));
|
||||
mu_assert_int_eq(string_size(input_data), stream_write_string(stream, input_data));
|
||||
mu_assert_int_eq(string_size(input_data), stream_size(stream));
|
||||
|
||||
const size_t substr_start = 8;
|
||||
const size_t substr_len = 11;
|
||||
|
||||
mu_check(stream_seek(stream, substr_start, StreamOffsetFromStart));
|
||||
mu_assert_int_eq(substr_start, stream_tell(stream));
|
||||
|
||||
// copy one substring from test data
|
||||
char test_substr[substr_len + 1];
|
||||
memset(test_substr, 0, substr_len + 1);
|
||||
memcpy(test_substr, stream_test_data + substr_start, substr_len);
|
||||
|
||||
char buf[substr_len + 1];
|
||||
memset(buf, 0, substr_len + 1);
|
||||
|
||||
// read substring
|
||||
mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
|
||||
mu_assert_string_eq(test_substr, buf);
|
||||
memset(buf, 0, substr_len + 1);
|
||||
|
||||
// forward seek to cause a cache miss
|
||||
mu_check(stream_seek(
|
||||
stream, (line_size + 1) * (rep_count - 1) - substr_len, StreamOffsetFromCurrent));
|
||||
// read same substring from a different line
|
||||
mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
|
||||
mu_assert_string_eq(test_substr, buf);
|
||||
memset(buf, 0, substr_len + 1);
|
||||
|
||||
// backward seek to cause a cache miss
|
||||
mu_check(stream_seek(
|
||||
stream, -((line_size + 1) * (rep_count - 1) + substr_len), StreamOffsetFromCurrent));
|
||||
mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
|
||||
mu_assert_string_eq(test_substr, buf);
|
||||
|
||||
// read the whole file
|
||||
mu_check(stream_rewind(stream));
|
||||
string_t tmp;
|
||||
string_init(tmp);
|
||||
while(stream_read_line(stream, tmp)) {
|
||||
string_cat(output_data, tmp);
|
||||
}
|
||||
string_clear(tmp);
|
||||
|
||||
// check against generated data
|
||||
mu_assert_int_eq(string_size(input_data), string_size(output_data));
|
||||
mu_check(string_equal_p(input_data, output_data));
|
||||
mu_check(stream_eof(stream));
|
||||
|
||||
stream_free(stream);
|
||||
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
string_clear(input_data);
|
||||
string_clear(output_data);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(stream_suite) {
|
||||
MU_RUN_TEST(stream_write_read_save_load_test);
|
||||
MU_RUN_TEST(stream_composite_test);
|
||||
MU_RUN_TEST(stream_split_test);
|
||||
MU_RUN_TEST(stream_buffered_write_after_read_test);
|
||||
MU_RUN_TEST(stream_buffered_large_file_test);
|
||||
}
|
||||
|
||||
int run_minunit_test_stream() {
|
||||
MU_RUN_SUITE(stream_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
627
applications/debug/unit_tests/subghz/subghz_test.c
Normal file
627
applications/debug/unit_tests/subghz/subghz_test.c
Normal file
@@ -0,0 +1,627 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../minunit.h"
|
||||
#include <lib/subghz/receiver.h>
|
||||
#include <lib/subghz/transmitter.h>
|
||||
#include <lib/subghz/subghz_keystore.h>
|
||||
#include <lib/subghz/subghz_file_encoder_worker.h>
|
||||
#include <lib/subghz/protocols/registry.h>
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
|
||||
#define TAG "SubGhz TEST"
|
||||
#define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes")
|
||||
#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
|
||||
#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
|
||||
#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
|
||||
#define TEST_RANDOM_COUNT_PARSE 232
|
||||
#define TEST_TIMEOUT 10000
|
||||
|
||||
static SubGhzEnvironment* environment_handler;
|
||||
static SubGhzReceiver* receiver_handler;
|
||||
//static SubGhzTransmitter* transmitter_handler;
|
||||
static SubGhzFileEncoderWorker* file_worker_encoder_handler;
|
||||
static uint16_t subghz_test_decoder_count = 0;
|
||||
|
||||
static void subghz_test_rx_callback(
|
||||
SubGhzReceiver* receiver,
|
||||
SubGhzProtocolDecoderBase* decoder_base,
|
||||
void* context) {
|
||||
UNUSED(receiver);
|
||||
UNUSED(context);
|
||||
string_t text;
|
||||
string_init(text);
|
||||
subghz_protocol_decoder_base_get_string(decoder_base, text);
|
||||
subghz_receiver_reset(receiver_handler);
|
||||
FURI_LOG_T(TAG, "\r\n%s", string_get_cstr(text));
|
||||
string_clear(text);
|
||||
subghz_test_decoder_count++;
|
||||
}
|
||||
|
||||
static void subghz_test_init(void) {
|
||||
environment_handler = subghz_environment_alloc();
|
||||
subghz_environment_set_came_atomo_rainbow_table_file_name(
|
||||
environment_handler, CAME_ATOMO_DIR_NAME);
|
||||
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
||||
environment_handler, NICE_FLOR_S_DIR_NAME);
|
||||
|
||||
receiver_handler = subghz_receiver_alloc_init(environment_handler);
|
||||
subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable);
|
||||
subghz_receiver_set_rx_callback(receiver_handler, subghz_test_rx_callback, NULL);
|
||||
}
|
||||
|
||||
static void subghz_test_deinit(void) {
|
||||
subghz_receiver_free(receiver_handler);
|
||||
subghz_environment_free(environment_handler);
|
||||
}
|
||||
|
||||
static bool subghz_decoder_test(const char* path, const char* name_decoder) {
|
||||
subghz_test_decoder_count = 0;
|
||||
uint32_t test_start = furi_get_tick();
|
||||
|
||||
SubGhzProtocolDecoderBase* decoder =
|
||||
subghz_receiver_search_decoder_base_by_name(receiver_handler, name_decoder);
|
||||
|
||||
if(decoder) {
|
||||
file_worker_encoder_handler = subghz_file_encoder_worker_alloc();
|
||||
if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path)) {
|
||||
// the worker needs a file in order to open and read part of the file
|
||||
furi_delay_ms(100);
|
||||
|
||||
LevelDuration level_duration;
|
||||
while(furi_get_tick() - test_start < TEST_TIMEOUT) {
|
||||
level_duration =
|
||||
subghz_file_encoder_worker_get_level_duration(file_worker_encoder_handler);
|
||||
if(!level_duration_is_reset(level_duration)) {
|
||||
bool level = level_duration_get_level(level_duration);
|
||||
uint32_t duration = level_duration_get_duration(level_duration);
|
||||
// Yield, to load data inside the worker
|
||||
furi_thread_yield();
|
||||
decoder->protocol->decoder->feed(decoder, level, duration);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
if(subghz_file_encoder_worker_is_running(file_worker_encoder_handler)) {
|
||||
subghz_file_encoder_worker_stop(file_worker_encoder_handler);
|
||||
}
|
||||
subghz_file_encoder_worker_free(file_worker_encoder_handler);
|
||||
}
|
||||
FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count);
|
||||
if(furi_get_tick() - test_start > TEST_TIMEOUT) {
|
||||
printf("\033[0;31mTest decoder %s ERROR TimeOut\033[0m\r\n", name_decoder);
|
||||
return false;
|
||||
} else {
|
||||
return subghz_test_decoder_count ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool subghz_decode_random_test(const char* path) {
|
||||
subghz_test_decoder_count = 0;
|
||||
subghz_receiver_reset(receiver_handler);
|
||||
uint32_t test_start = furi_get_tick();
|
||||
|
||||
file_worker_encoder_handler = subghz_file_encoder_worker_alloc();
|
||||
if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path)) {
|
||||
// the worker needs a file in order to open and read part of the file
|
||||
furi_delay_ms(100);
|
||||
|
||||
LevelDuration level_duration;
|
||||
while(furi_get_tick() - test_start < TEST_TIMEOUT * 10) {
|
||||
level_duration =
|
||||
subghz_file_encoder_worker_get_level_duration(file_worker_encoder_handler);
|
||||
if(!level_duration_is_reset(level_duration)) {
|
||||
bool level = level_duration_get_level(level_duration);
|
||||
uint32_t duration = level_duration_get_duration(level_duration);
|
||||
// Yield, to load data inside the worker
|
||||
furi_thread_yield();
|
||||
subghz_receiver_decode(receiver_handler, level, duration);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
furi_delay_ms(10);
|
||||
if(subghz_file_encoder_worker_is_running(file_worker_encoder_handler)) {
|
||||
subghz_file_encoder_worker_stop(file_worker_encoder_handler);
|
||||
}
|
||||
subghz_file_encoder_worker_free(file_worker_encoder_handler);
|
||||
}
|
||||
FURI_LOG_D(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count);
|
||||
if(furi_get_tick() - test_start > TEST_TIMEOUT * 10) {
|
||||
printf("\033[0;31mRandom test ERROR TimeOut\033[0m\r\n");
|
||||
return false;
|
||||
} else if(subghz_test_decoder_count == TEST_RANDOM_COUNT_PARSE) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool subghz_encoder_test(const char* path) {
|
||||
subghz_test_decoder_count = 0;
|
||||
uint32_t test_start = furi_get_tick();
|
||||
string_t temp_str;
|
||||
string_init(temp_str);
|
||||
bool file_load = false;
|
||||
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
|
||||
|
||||
do {
|
||||
if(!flipper_format_file_open_existing(fff_data_file, path)) {
|
||||
FURI_LOG_E(TAG, "Error open file %s", path);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) {
|
||||
FURI_LOG_E(TAG, "Missing Preset");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) {
|
||||
FURI_LOG_E(TAG, "Missing Protocol");
|
||||
break;
|
||||
}
|
||||
file_load = true;
|
||||
} while(false);
|
||||
if(file_load) {
|
||||
SubGhzTransmitter* transmitter =
|
||||
subghz_transmitter_alloc_init(environment_handler, string_get_cstr(temp_str));
|
||||
subghz_transmitter_deserialize(transmitter, fff_data_file);
|
||||
|
||||
SubGhzProtocolDecoderBase* decoder = subghz_receiver_search_decoder_base_by_name(
|
||||
receiver_handler, string_get_cstr(temp_str));
|
||||
|
||||
if(decoder) {
|
||||
LevelDuration level_duration;
|
||||
while(furi_get_tick() - test_start < TEST_TIMEOUT) {
|
||||
level_duration = subghz_transmitter_yield(transmitter);
|
||||
if(!level_duration_is_reset(level_duration)) {
|
||||
bool level = level_duration_get_level(level_duration);
|
||||
uint32_t duration = level_duration_get_duration(level_duration);
|
||||
decoder->protocol->decoder->feed(decoder, level, duration);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
subghz_transmitter_free(transmitter);
|
||||
}
|
||||
flipper_format_free(fff_data_file);
|
||||
FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count);
|
||||
if(furi_get_tick() - test_start > TEST_TIMEOUT) {
|
||||
printf("\033[0;31mTest encoder %s ERROR TimeOut\033[0m\r\n", string_get_cstr(temp_str));
|
||||
subghz_test_decoder_count = 0;
|
||||
}
|
||||
string_clear(temp_str);
|
||||
|
||||
return subghz_test_decoder_count ? true : false;
|
||||
}
|
||||
|
||||
MU_TEST(subghz_keystore_test) {
|
||||
mu_assert(
|
||||
subghz_environment_load_keystore(environment_handler, KEYSTORE_DIR_NAME),
|
||||
"Test keystore error");
|
||||
}
|
||||
|
||||
//test decoders
|
||||
MU_TEST(subghz_decoder_came_atomo_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/came_atomo_raw.sub"), SUBGHZ_PROTOCOL_CAME_ATOMO_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_CAME_ATOMO_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_came_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(EXT_PATH("unit_tests/subghz/came_raw.sub"), SUBGHZ_PROTOCOL_CAME_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_CAME_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_came_twee_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/came_twee_raw.sub"), SUBGHZ_PROTOCOL_CAME_TWEE_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_CAME_TWEE_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_faac_slh_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/faac_slh_raw.sub"), SUBGHZ_PROTOCOL_FAAC_SLH_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_FAAC_SLH_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_gate_tx_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/gate_tx_raw.sub"), SUBGHZ_PROTOCOL_GATE_TX_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_GATE_TX_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_hormann_hsm_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/hormann_hsm_raw.sub"), SUBGHZ_PROTOCOL_HORMANN_HSM_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_HORMANN_HSM_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_ido_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/ido_117_111_raw.sub"), SUBGHZ_PROTOCOL_IDO_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_IDO_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_keelog_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/doorhan_raw.sub"), SUBGHZ_PROTOCOL_KEELOQ_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_kia_seed_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/kia_seed_raw.sub"), SUBGHZ_PROTOCOL_KIA_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_KIA_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_nero_radio_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/nero_radio_raw.sub"), SUBGHZ_PROTOCOL_NERO_RADIO_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_NERO_RADIO_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_nero_sketch_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/nero_sketch_raw.sub"), SUBGHZ_PROTOCOL_NERO_SKETCH_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_NERO_SKETCH_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_nice_flo_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/nice_flo_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLO_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_nice_flor_s_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/nice_flor_s_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_princeton_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/Princeton_raw.sub"), SUBGHZ_PROTOCOL_PRINCETON_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_PRINCETON_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_scher_khan_magic_code_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/scher_khan_magic_code.sub"),
|
||||
SUBGHZ_PROTOCOL_SCHER_KHAN_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_SCHER_KHAN_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_somfy_keytis_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/Somfy_keytis_raw.sub"), SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_somfy_telis_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/somfy_telis_raw.sub"), SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_star_line_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/cenmax_raw.sub"), SUBGHZ_PROTOCOL_STAR_LINE_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_STAR_LINE_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_linear_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/linear_raw.sub"), SUBGHZ_PROTOCOL_LINEAR_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_megacode_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/megacode_raw.sub"), SUBGHZ_PROTOCOL_MEGACODE_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_MEGACODE_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_secplus_v1_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/security_pls_1_0_raw.sub"),
|
||||
SUBGHZ_PROTOCOL_SECPLUS_V1_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_SECPLUS_V1_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_secplus_v2_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/security_pls_2_0_raw.sub"),
|
||||
SUBGHZ_PROTOCOL_SECPLUS_V2_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_SECPLUS_V2_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_holtek_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/holtek_raw.sub"), SUBGHZ_PROTOCOL_HOLTEK_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_HOLTEK_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_power_smart_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/power_smart_raw.sub"), SUBGHZ_PROTOCOL_POWER_SMART_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_POWER_SMART_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_marantec_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/marantec_raw.sub"), SUBGHZ_PROTOCOL_MARANTEC_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_MARANTEC_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_bett_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(EXT_PATH("unit_tests/subghz/bett_raw.sub"), SUBGHZ_PROTOCOL_BETT_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_BETT_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_doitrand_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/doitrand_raw.sub"), SUBGHZ_PROTOCOL_DOITRAND_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_DOITRAND_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_phoenix_v2_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/phoenix_v2_raw.sub"), SUBGHZ_PROTOCOL_PHOENIX_V2_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_PHOENIX_V2_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_honeywell_wdb_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/honeywell_wdb_raw.sub"),
|
||||
SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_magellen_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/magellen_raw.sub"), SUBGHZ_PROTOCOL_MAGELLEN_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_intertechno_v3_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/intertechno_v3_raw.sub"),
|
||||
SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_decoder_clemsa_test) {
|
||||
mu_assert(
|
||||
subghz_decoder_test(
|
||||
EXT_PATH("unit_tests/subghz/clemsa_raw.sub"), SUBGHZ_PROTOCOL_CLEMSA_NAME),
|
||||
"Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
|
||||
}
|
||||
|
||||
//test encoders
|
||||
MU_TEST(subghz_encoder_princeton_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/princeton.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_PRINCETON_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_came_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/came.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_CAME_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_came_twee_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/came_twee.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_CAME_TWEE_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_gate_tx_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/gate_tx.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_GATE_TX_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_nice_flo_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/nice_flo.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_keelog_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/doorhan.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_linear_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/linear.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_megacode_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/megacode.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_MEGACODE_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_holtek_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/holtek.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_HOLTEK_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_secplus_v1_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/security_pls_1_0.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_SECPLUS_V1_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_secplus_v2_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/security_pls_2_0.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_SECPLUS_V2_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_power_smart_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/power_smart.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_POWER_SMART_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_marantec_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/marantec.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_MARANTEC_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_bett_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/bett.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_BETT_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_doitrand_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/doitrand.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_DOITRAND_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_phoenix_v2_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/phoenix_v2.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_PHOENIX_V2_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_honeywell_wdb_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/honeywell_wdb.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_magellen_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellen.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_MAGELLEN_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_intertechno_v3_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/intertechno_v3.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_encoder_clemsa_test) {
|
||||
mu_assert(
|
||||
subghz_encoder_test(EXT_PATH("unit_tests/subghz/clemsa.sub")),
|
||||
"Test encoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST(subghz_random_test) {
|
||||
mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(subghz) {
|
||||
subghz_test_init();
|
||||
MU_RUN_TEST(subghz_keystore_test);
|
||||
|
||||
MU_RUN_TEST(subghz_decoder_came_atomo_test);
|
||||
MU_RUN_TEST(subghz_decoder_came_test);
|
||||
MU_RUN_TEST(subghz_decoder_came_twee_test);
|
||||
MU_RUN_TEST(subghz_decoder_faac_slh_test);
|
||||
MU_RUN_TEST(subghz_decoder_gate_tx_test);
|
||||
MU_RUN_TEST(subghz_decoder_hormann_hsm_test);
|
||||
MU_RUN_TEST(subghz_decoder_ido_test);
|
||||
MU_RUN_TEST(subghz_decoder_keelog_test);
|
||||
MU_RUN_TEST(subghz_decoder_kia_seed_test);
|
||||
MU_RUN_TEST(subghz_decoder_nero_radio_test);
|
||||
MU_RUN_TEST(subghz_decoder_nero_sketch_test);
|
||||
MU_RUN_TEST(subghz_decoder_nice_flo_test);
|
||||
MU_RUN_TEST(subghz_decoder_nice_flor_s_test);
|
||||
MU_RUN_TEST(subghz_decoder_princeton_test);
|
||||
MU_RUN_TEST(subghz_decoder_scher_khan_magic_code_test);
|
||||
MU_RUN_TEST(subghz_decoder_somfy_keytis_test);
|
||||
MU_RUN_TEST(subghz_decoder_somfy_telis_test);
|
||||
MU_RUN_TEST(subghz_decoder_star_line_test);
|
||||
MU_RUN_TEST(subghz_decoder_linear_test);
|
||||
MU_RUN_TEST(subghz_decoder_megacode_test);
|
||||
MU_RUN_TEST(subghz_decoder_secplus_v1_test);
|
||||
MU_RUN_TEST(subghz_decoder_secplus_v2_test);
|
||||
MU_RUN_TEST(subghz_decoder_holtek_test);
|
||||
MU_RUN_TEST(subghz_decoder_power_smart_test);
|
||||
MU_RUN_TEST(subghz_decoder_marantec_test);
|
||||
MU_RUN_TEST(subghz_decoder_bett_test);
|
||||
MU_RUN_TEST(subghz_decoder_doitrand_test);
|
||||
MU_RUN_TEST(subghz_decoder_phoenix_v2_test);
|
||||
MU_RUN_TEST(subghz_decoder_honeywell_wdb_test);
|
||||
MU_RUN_TEST(subghz_decoder_magellen_test);
|
||||
MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
|
||||
MU_RUN_TEST(subghz_decoder_clemsa_test);
|
||||
|
||||
MU_RUN_TEST(subghz_encoder_princeton_test);
|
||||
MU_RUN_TEST(subghz_encoder_came_test);
|
||||
MU_RUN_TEST(subghz_encoder_came_twee_test);
|
||||
MU_RUN_TEST(subghz_encoder_gate_tx_test);
|
||||
MU_RUN_TEST(subghz_encoder_nice_flo_test);
|
||||
MU_RUN_TEST(subghz_encoder_keelog_test);
|
||||
MU_RUN_TEST(subghz_encoder_linear_test);
|
||||
MU_RUN_TEST(subghz_encoder_megacode_test);
|
||||
MU_RUN_TEST(subghz_encoder_holtek_test);
|
||||
MU_RUN_TEST(subghz_encoder_secplus_v1_test);
|
||||
MU_RUN_TEST(subghz_encoder_secplus_v2_test);
|
||||
MU_RUN_TEST(subghz_encoder_power_smart_test);
|
||||
MU_RUN_TEST(subghz_encoder_marantec_test);
|
||||
MU_RUN_TEST(subghz_encoder_bett_test);
|
||||
MU_RUN_TEST(subghz_encoder_doitrand_test);
|
||||
MU_RUN_TEST(subghz_encoder_phoenix_v2_test);
|
||||
MU_RUN_TEST(subghz_encoder_honeywell_wdb_test);
|
||||
MU_RUN_TEST(subghz_encoder_magellen_test);
|
||||
MU_RUN_TEST(subghz_encoder_intertechno_v3_test);
|
||||
MU_RUN_TEST(subghz_encoder_clemsa_test);
|
||||
|
||||
MU_RUN_TEST(subghz_random_test);
|
||||
subghz_test_deinit();
|
||||
}
|
||||
|
||||
int run_minunit_test_subghz() {
|
||||
MU_RUN_SUITE(subghz);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
137
applications/debug/unit_tests/test_index.c
Normal file
137
applications/debug/unit_tests/test_index.c
Normal file
@@ -0,0 +1,137 @@
|
||||
#include "m-string.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "minunit_vars.h"
|
||||
#include <notification/notification_messages.h>
|
||||
#include <cli/cli.h>
|
||||
#include <loader/loader.h>
|
||||
|
||||
#define TAG "UnitTests"
|
||||
|
||||
int run_minunit_test_furi();
|
||||
int run_minunit_test_infrared();
|
||||
int run_minunit_test_rpc();
|
||||
int run_minunit_test_flipper_format();
|
||||
int run_minunit_test_flipper_format_string();
|
||||
int run_minunit_test_stream();
|
||||
int run_minunit_test_storage();
|
||||
int run_minunit_test_subghz();
|
||||
int run_minunit_test_dirwalk();
|
||||
int run_minunit_test_protocol_dict();
|
||||
int run_minunit_test_lfrfid_protocols();
|
||||
int run_minunit_test_nfc();
|
||||
int run_minunit_test_bit_lib();
|
||||
|
||||
typedef int (*UnitTestEntry)();
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
const UnitTestEntry entry;
|
||||
} UnitTest;
|
||||
|
||||
const UnitTest unit_tests[] = {
|
||||
{.name = "furi", .entry = run_minunit_test_furi},
|
||||
{.name = "storage", .entry = run_minunit_test_storage},
|
||||
{.name = "stream", .entry = run_minunit_test_stream},
|
||||
{.name = "dirwalk", .entry = run_minunit_test_dirwalk},
|
||||
{.name = "flipper_format", .entry = run_minunit_test_flipper_format},
|
||||
{.name = "flipper_format_string", .entry = run_minunit_test_flipper_format_string},
|
||||
{.name = "rpc", .entry = run_minunit_test_rpc},
|
||||
{.name = "subghz", .entry = run_minunit_test_subghz},
|
||||
{.name = "infrared", .entry = run_minunit_test_infrared},
|
||||
{.name = "nfc", .entry = run_minunit_test_nfc},
|
||||
{.name = "protocol_dict", .entry = run_minunit_test_protocol_dict},
|
||||
{.name = "lfrfid", .entry = run_minunit_test_lfrfid_protocols},
|
||||
{.name = "bit_lib", .entry = run_minunit_test_bit_lib},
|
||||
};
|
||||
|
||||
void minunit_print_progress() {
|
||||
static const char progress[] = {'\\', '|', '/', '-'};
|
||||
static uint8_t progress_counter = 0;
|
||||
static TickType_t last_tick = 0;
|
||||
TickType_t current_tick = xTaskGetTickCount();
|
||||
if(current_tick - last_tick > 20) {
|
||||
last_tick = current_tick;
|
||||
printf("[%c]\033[3D", progress[++progress_counter % COUNT_OF(progress)]);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
void minunit_print_fail(const char* str) {
|
||||
printf(FURI_LOG_CLR_E "%s\r\n" FURI_LOG_CLR_RESET, str);
|
||||
}
|
||||
|
||||
void unit_tests_cli(Cli* cli, string_t args, void* context) {
|
||||
UNUSED(cli);
|
||||
UNUSED(args);
|
||||
UNUSED(context);
|
||||
uint32_t failed_tests = 0;
|
||||
minunit_run = 0;
|
||||
minunit_assert = 0;
|
||||
minunit_fail = 0;
|
||||
minunit_status = 0;
|
||||
|
||||
Loader* loader = furi_record_open(RECORD_LOADER);
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
// TODO: lock device while test running
|
||||
if(loader_is_locked(loader)) {
|
||||
printf("RPC: stop all applications to run tests\r\n");
|
||||
notification_message(notification, &sequence_blink_magenta_100);
|
||||
} else {
|
||||
notification_message_block(notification, &sequence_set_only_blue_255);
|
||||
|
||||
uint32_t heap_before = memmgr_get_free_heap();
|
||||
uint32_t cycle_counter = furi_get_tick();
|
||||
|
||||
for(size_t i = 0; i < COUNT_OF(unit_tests); i++) {
|
||||
if(cli_cmd_interrupt_received(cli)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(string_size(args)) {
|
||||
if(string_cmp_str(args, unit_tests[i].name) == 0) {
|
||||
failed_tests += unit_tests[i].entry();
|
||||
} else {
|
||||
printf("Skipping %s\r\n", unit_tests[i].name);
|
||||
}
|
||||
} else {
|
||||
failed_tests += unit_tests[i].entry();
|
||||
}
|
||||
}
|
||||
printf("\r\nFailed tests: %lu\r\n", failed_tests);
|
||||
|
||||
// Time report
|
||||
cycle_counter = (furi_get_tick() - cycle_counter);
|
||||
printf("Consumed: %lu ms\r\n", cycle_counter);
|
||||
|
||||
// Wait for tested services and apps to deallocate memory
|
||||
furi_delay_ms(200);
|
||||
uint32_t heap_after = memmgr_get_free_heap();
|
||||
printf("Leaked: %ld\r\n", heap_before - heap_after);
|
||||
|
||||
// Final Report
|
||||
if(failed_tests == 0) {
|
||||
notification_message(notification, &sequence_success);
|
||||
printf("Status: PASSED\r\n");
|
||||
} else {
|
||||
notification_message(notification, &sequence_error);
|
||||
printf("Status: FAILED\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
furi_record_close(RECORD_LOADER);
|
||||
}
|
||||
|
||||
void unit_tests_on_system_start() {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open(RECORD_CLI);
|
||||
|
||||
// We need to launch apps from tests, so we cannot lock loader
|
||||
cli_add_command(cli, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL);
|
||||
furi_record_close(RECORD_CLI);
|
||||
#endif
|
||||
}
|
88
applications/debug/unit_tests/varint/varint_test.c
Normal file
88
applications/debug/unit_tests/varint/varint_test.c
Normal file
@@ -0,0 +1,88 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../minunit.h"
|
||||
#include <toolbox/varint.h>
|
||||
#include <toolbox/profiler.h>
|
||||
|
||||
MU_TEST(test_varint_basic_u) {
|
||||
mu_assert_int_eq(1, varint_uint32_length(0));
|
||||
mu_assert_int_eq(5, varint_uint32_length(UINT32_MAX));
|
||||
|
||||
uint8_t data[8] = {};
|
||||
uint32_t out_value;
|
||||
|
||||
mu_assert_int_eq(1, varint_uint32_pack(0, data));
|
||||
mu_assert_int_eq(1, varint_uint32_unpack(&out_value, data, 8));
|
||||
mu_assert_int_eq(0, out_value);
|
||||
|
||||
mu_assert_int_eq(5, varint_uint32_pack(UINT32_MAX, data));
|
||||
mu_assert_int_eq(5, varint_uint32_unpack(&out_value, data, 8));
|
||||
mu_assert_int_eq(UINT32_MAX, out_value);
|
||||
}
|
||||
|
||||
MU_TEST(test_varint_basic_i) {
|
||||
mu_assert_int_eq(5, varint_int32_length(INT32_MIN / 2));
|
||||
mu_assert_int_eq(1, varint_int32_length(0));
|
||||
mu_assert_int_eq(5, varint_int32_length(INT32_MAX / 2));
|
||||
|
||||
mu_assert_int_eq(2, varint_int32_length(127));
|
||||
mu_assert_int_eq(2, varint_int32_length(-127));
|
||||
|
||||
uint8_t data[8] = {};
|
||||
int32_t out_value;
|
||||
mu_assert_int_eq(1, varint_int32_pack(0, data));
|
||||
mu_assert_int_eq(1, varint_int32_unpack(&out_value, data, 8));
|
||||
mu_assert_int_eq(0, out_value);
|
||||
|
||||
mu_assert_int_eq(2, varint_int32_pack(127, data));
|
||||
mu_assert_int_eq(2, varint_int32_unpack(&out_value, data, 8));
|
||||
mu_assert_int_eq(127, out_value);
|
||||
|
||||
mu_assert_int_eq(2, varint_int32_pack(-127, data));
|
||||
mu_assert_int_eq(2, varint_int32_unpack(&out_value, data, 8));
|
||||
mu_assert_int_eq(-127, out_value);
|
||||
|
||||
mu_assert_int_eq(5, varint_int32_pack(INT32_MAX, data));
|
||||
mu_assert_int_eq(5, varint_int32_unpack(&out_value, data, 8));
|
||||
mu_assert_int_eq(INT32_MAX, out_value);
|
||||
|
||||
mu_assert_int_eq(5, varint_int32_pack(INT32_MIN / 2 + 1, data));
|
||||
mu_assert_int_eq(5, varint_int32_unpack(&out_value, data, 8));
|
||||
mu_assert_int_eq(INT32_MIN / 2 + 1, out_value);
|
||||
}
|
||||
|
||||
MU_TEST(test_varint_rand_u) {
|
||||
uint8_t data[8] = {};
|
||||
uint32_t out_value;
|
||||
|
||||
for(size_t i = 0; i < 200000; i++) {
|
||||
uint32_t rand_value = rand();
|
||||
mu_assert_int_eq(
|
||||
varint_uint32_pack(rand_value, data), varint_uint32_unpack(&out_value, data, 8));
|
||||
mu_assert_int_eq(rand_value, out_value);
|
||||
}
|
||||
}
|
||||
|
||||
MU_TEST(test_varint_rand_i) {
|
||||
uint8_t data[8] = {};
|
||||
int32_t out_value;
|
||||
|
||||
for(size_t i = 0; i < 200000; i++) {
|
||||
int32_t rand_value = rand() + (INT32_MIN / 2 + 1);
|
||||
mu_assert_int_eq(
|
||||
varint_int32_pack(rand_value, data), varint_int32_unpack(&out_value, data, 8));
|
||||
mu_assert_int_eq(rand_value, out_value);
|
||||
}
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(test_varint_suite) {
|
||||
MU_RUN_TEST(test_varint_basic_u);
|
||||
MU_RUN_TEST(test_varint_basic_i);
|
||||
MU_RUN_TEST(test_varint_rand_u);
|
||||
MU_RUN_TEST(test_varint_rand_i);
|
||||
}
|
||||
|
||||
int run_minunit_test_varint() {
|
||||
MU_RUN_SUITE(test_varint_suite);
|
||||
return MU_EXIT_CODE;
|
||||
}
|
11
applications/debug/usb_mouse/application.fam
Normal file
11
applications/debug/usb_mouse/application.fam
Normal file
@@ -0,0 +1,11 @@
|
||||
App(
|
||||
appid="usb_mouse",
|
||||
name="USB Mouse Demo",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="usb_mouse_app",
|
||||
cdefines=["APP_USB_MOUSE"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=60,
|
||||
fap_category="Debug",
|
||||
)
|
124
applications/debug/usb_mouse/usb_mouse.c
Normal file
124
applications/debug/usb_mouse/usb_mouse.c
Normal file
@@ -0,0 +1,124 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
|
||||
#define MOUSE_MOVE_SHORT 5
|
||||
#define MOUSE_MOVE_LONG 20
|
||||
|
||||
typedef enum {
|
||||
EventTypeInput,
|
||||
} EventType;
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
InputEvent input;
|
||||
};
|
||||
EventType type;
|
||||
} UsbMouseEvent;
|
||||
|
||||
static void usb_mouse_render_callback(Canvas* canvas, void* ctx) {
|
||||
UNUSED(ctx);
|
||||
canvas_clear(canvas);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 0, 10, "USB Mouse Demo");
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 0, 63, "Hold [back] to exit");
|
||||
}
|
||||
|
||||
static void usb_mouse_input_callback(InputEvent* input_event, void* ctx) {
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
|
||||
UsbMouseEvent event;
|
||||
event.type = EventTypeInput;
|
||||
event.input = *input_event;
|
||||
furi_message_queue_put(event_queue, &event, FuriWaitForever);
|
||||
}
|
||||
|
||||
int32_t usb_mouse_app(void* p) {
|
||||
UNUSED(p);
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(UsbMouseEvent));
|
||||
furi_check(event_queue);
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
|
||||
FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();
|
||||
furi_hal_usb_unlock();
|
||||
furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true);
|
||||
|
||||
view_port_draw_callback_set(view_port, usb_mouse_render_callback, NULL);
|
||||
view_port_input_callback_set(view_port, usb_mouse_input_callback, event_queue);
|
||||
|
||||
// Open GUI and register view_port
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
UsbMouseEvent event;
|
||||
while(1) {
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever);
|
||||
|
||||
if(event_status == FuriStatusOk) {
|
||||
if(event.type == EventTypeInput) {
|
||||
if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) {
|
||||
furi_hal_hid_mouse_press(HID_MOUSE_BTN_RIGHT);
|
||||
furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT);
|
||||
}
|
||||
|
||||
if(event.input.key == InputKeyOk) {
|
||||
if(event.input.type == InputTypePress) {
|
||||
furi_hal_hid_mouse_press(HID_MOUSE_BTN_LEFT);
|
||||
} else if(event.input.type == InputTypeRelease) {
|
||||
furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT);
|
||||
}
|
||||
}
|
||||
|
||||
if(event.input.key == InputKeyRight) {
|
||||
if(event.input.type == InputTypePress) {
|
||||
furi_hal_hid_mouse_move(MOUSE_MOVE_SHORT, 0);
|
||||
} else if(event.input.type == InputTypeRepeat) {
|
||||
furi_hal_hid_mouse_move(MOUSE_MOVE_LONG, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if(event.input.key == InputKeyLeft) {
|
||||
if(event.input.type == InputTypePress) {
|
||||
furi_hal_hid_mouse_move(-MOUSE_MOVE_SHORT, 0);
|
||||
} else if(event.input.type == InputTypeRepeat) {
|
||||
furi_hal_hid_mouse_move(-MOUSE_MOVE_LONG, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if(event.input.key == InputKeyDown) {
|
||||
if(event.input.type == InputTypePress) {
|
||||
furi_hal_hid_mouse_move(0, MOUSE_MOVE_SHORT);
|
||||
} else if(event.input.type == InputTypeRepeat) {
|
||||
furi_hal_hid_mouse_move(0, MOUSE_MOVE_LONG);
|
||||
}
|
||||
}
|
||||
|
||||
if(event.input.key == InputKeyUp) {
|
||||
if(event.input.type == InputTypePress) {
|
||||
furi_hal_hid_mouse_move(0, -MOUSE_MOVE_SHORT);
|
||||
} else if(event.input.type == InputTypeRepeat) {
|
||||
furi_hal_hid_mouse_move(0, -MOUSE_MOVE_LONG);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
view_port_update(view_port);
|
||||
}
|
||||
|
||||
furi_hal_usb_set_config(usb_mode_prev, NULL);
|
||||
|
||||
// remove & free all stuff created by app
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
|
||||
return 0;
|
||||
}
|
11
applications/debug/usb_test/application.fam
Normal file
11
applications/debug/usb_test/application.fam
Normal file
@@ -0,0 +1,11 @@
|
||||
App(
|
||||
appid="usb_test",
|
||||
name="USB Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="usb_test_app",
|
||||
cdefines=["APP_USB_TEST"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=50,
|
||||
fap_category="Debug",
|
||||
)
|
124
applications/debug/usb_test/usb_test.c
Normal file
124
applications/debug/usb_test/usb_test.c
Normal file
@@ -0,0 +1,124 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <gui/view.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/gui.h>
|
||||
|
||||
typedef struct {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
Submenu* submenu;
|
||||
FuriHalUsbHidConfig hid_cfg;
|
||||
} UsbTestApp;
|
||||
|
||||
typedef enum {
|
||||
UsbTestSubmenuIndexEnable,
|
||||
UsbTestSubmenuIndexDisable,
|
||||
UsbTestSubmenuIndexRestart,
|
||||
UsbTestSubmenuIndexVcpSingle,
|
||||
UsbTestSubmenuIndexVcpDual,
|
||||
UsbTestSubmenuIndexHid,
|
||||
UsbTestSubmenuIndexHidWithParams,
|
||||
UsbTestSubmenuIndexHidU2F,
|
||||
} SubmenuIndex;
|
||||
|
||||
void usb_test_submenu_callback(void* context, uint32_t index) {
|
||||
furi_assert(context);
|
||||
UsbTestApp* app = context;
|
||||
if(index == UsbTestSubmenuIndexEnable) {
|
||||
furi_hal_usb_enable();
|
||||
} else if(index == UsbTestSubmenuIndexDisable) {
|
||||
furi_hal_usb_disable();
|
||||
} else if(index == UsbTestSubmenuIndexRestart) {
|
||||
furi_hal_usb_reinit();
|
||||
} else if(index == UsbTestSubmenuIndexVcpSingle) {
|
||||
furi_hal_usb_set_config(&usb_cdc_single, NULL);
|
||||
} else if(index == UsbTestSubmenuIndexVcpDual) {
|
||||
furi_hal_usb_set_config(&usb_cdc_dual, NULL);
|
||||
} else if(index == UsbTestSubmenuIndexHid) {
|
||||
furi_hal_usb_set_config(&usb_hid, NULL);
|
||||
} else if(index == UsbTestSubmenuIndexHidWithParams) {
|
||||
app->hid_cfg.vid = 0x1234;
|
||||
app->hid_cfg.pid = 0xabcd;
|
||||
strlcpy(app->hid_cfg.manuf, "WEN", sizeof(app->hid_cfg.manuf));
|
||||
strlcpy(app->hid_cfg.product, "FLIP", sizeof(app->hid_cfg.product));
|
||||
furi_hal_usb_set_config(&usb_hid, &app->hid_cfg);
|
||||
} else if(index == UsbTestSubmenuIndexHidU2F) {
|
||||
furi_hal_usb_set_config(&usb_hid_u2f, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t usb_test_exit(void* context) {
|
||||
UNUSED(context);
|
||||
return VIEW_NONE;
|
||||
}
|
||||
|
||||
UsbTestApp* usb_test_app_alloc() {
|
||||
UsbTestApp* app = malloc(sizeof(UsbTestApp));
|
||||
|
||||
// Gui
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
// View dispatcher
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// Views
|
||||
app->submenu = submenu_alloc();
|
||||
submenu_add_item(
|
||||
app->submenu, "Enable", UsbTestSubmenuIndexEnable, usb_test_submenu_callback, app);
|
||||
submenu_add_item(
|
||||
app->submenu, "Disable", UsbTestSubmenuIndexDisable, usb_test_submenu_callback, app);
|
||||
submenu_add_item(
|
||||
app->submenu, "Restart", UsbTestSubmenuIndexRestart, usb_test_submenu_callback, app);
|
||||
submenu_add_item(
|
||||
app->submenu, "Single VCP", UsbTestSubmenuIndexVcpSingle, usb_test_submenu_callback, app);
|
||||
submenu_add_item(
|
||||
app->submenu, "Dual VCP", UsbTestSubmenuIndexVcpDual, usb_test_submenu_callback, app);
|
||||
submenu_add_item(
|
||||
app->submenu, "HID KB+Mouse", UsbTestSubmenuIndexHid, usb_test_submenu_callback, app);
|
||||
submenu_add_item(
|
||||
app->submenu,
|
||||
"HID KB+Mouse custom ID",
|
||||
UsbTestSubmenuIndexHidWithParams,
|
||||
usb_test_submenu_callback,
|
||||
app);
|
||||
submenu_add_item(
|
||||
app->submenu, "HID U2F", UsbTestSubmenuIndexHidU2F, usb_test_submenu_callback, app);
|
||||
view_set_previous_callback(submenu_get_view(app->submenu), usb_test_exit);
|
||||
view_dispatcher_add_view(app->view_dispatcher, 0, submenu_get_view(app->submenu));
|
||||
|
||||
// Switch to menu
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, 0);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void usb_test_app_free(UsbTestApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// Free views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, 0);
|
||||
submenu_free(app->submenu);
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
|
||||
// Close gui record
|
||||
furi_record_close(RECORD_GUI);
|
||||
app->gui = NULL;
|
||||
|
||||
// Free rest
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t usb_test_app(void* p) {
|
||||
UNUSED(p);
|
||||
UsbTestApp* app = usb_test_app_alloc();
|
||||
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
|
||||
usb_test_app_free(app);
|
||||
return 0;
|
||||
}
|
11
applications/debug/vibro_test/application.fam
Normal file
11
applications/debug/vibro_test/application.fam
Normal file
@@ -0,0 +1,11 @@
|
||||
App(
|
||||
appid="vibro_test",
|
||||
name="Vibro Test",
|
||||
apptype=FlipperAppType.DEBUG,
|
||||
entry_point="vibro_test_app",
|
||||
cdefines=["APP_VIBRO_TEST"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=20,
|
||||
fap_category="Debug",
|
||||
)
|
67
applications/debug/vibro_test/vibro_test.c
Normal file
67
applications/debug/vibro_test/vibro_test.c
Normal file
@@ -0,0 +1,67 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
void vibro_test_draw_callback(Canvas* canvas, void* ctx) {
|
||||
UNUSED(ctx);
|
||||
canvas_clear(canvas);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 2, 10, "Vibro application");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 2, 22, "Press OK turns on vibro");
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, 2, 34, "Release OK turns off vibro");
|
||||
}
|
||||
|
||||
void vibro_test_input_callback(InputEvent* input_event, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
|
||||
}
|
||||
|
||||
int32_t vibro_test_app(void* p) {
|
||||
UNUSED(p);
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
|
||||
// Configure view port
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, vibro_test_draw_callback, NULL);
|
||||
view_port_input_callback_set(view_port, vibro_test_input_callback, event_queue);
|
||||
|
||||
// Register view port in GUI
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
InputEvent event;
|
||||
|
||||
while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
|
||||
if(event.type == InputTypeShort && event.key == InputKeyBack) {
|
||||
notification_message(notification, &sequence_reset_vibro);
|
||||
notification_message(notification, &sequence_reset_green);
|
||||
break;
|
||||
}
|
||||
if(event.key == InputKeyOk) {
|
||||
if(event.type == InputTypePress) {
|
||||
notification_message(notification, &sequence_set_vibro_on);
|
||||
notification_message(notification, &sequence_set_green_255);
|
||||
} else if(event.type == InputTypeRelease) {
|
||||
notification_message(notification, &sequence_reset_vibro);
|
||||
notification_message(notification, &sequence_reset_green);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user