-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: backport js_gpio from unleashed * feat: backport js_keyboard, TextInputModel::minimum_length from unleashed * fix: api version inconsistency * style: js_gpio * build: fix submodule ._ . * refactor: js_gpio * docs: type declarations for gpio * feat: gpio interrupts * fix: js_gpio freeing, resetting and minor stylistic changes * style: js_gpio * style: mlib array, fixme's * feat: js_gpio adc * feat: js_event_loop * docs: js_event_loop * feat: js_event_loop subscription cancellation * feat: js_event_loop + js_gpio integration * fix: js_event_loop memory leak * feat: stop event loop on back button * test: js: basic, math, event_loop * feat: js_event_loop queue * feat: js linkage to previously loaded plugins * build: fix ci errors * feat: js module ordered teardown * feat: js_gui_defer_free * feat: basic hourglass view * style: JS ASS (Argument Schema for Scripts) * fix: js_event_loop mem leaks and lifetime problems * fix: crashing test and pvs false positives * feat: mjs custom obj destructors, gui submenu view * refactor: yank js_gui_defer_free (yuck) * refactor: maybe_unsubscribe * empty_screen, docs, typing fix-ups * docs: navigation event & demo * feat: submenu setHeader * feat: text_input * feat: text_box * docs: text_box availability * ci: silence irrelevant pvs low priority warning * style: use furistring * style: _get_at -> _safe_get * fix: built-in module name assignment * feat: js_dialog; refactor, optimize: js_gui * docs: js_gui * ci: silence pvs warning: Memory allocation is infallible * style: fix storage spelling * feat: foreign pointer signature checks * feat: js_storage * docs: js_storage * fix: my unit test was breaking other tests ;_; * ci: fix ci? * Make doxygen happy * docs: flipper, math, notification, global * style: review suggestions * style: review fixups * fix: badusb demo script * docs: badusb * ci: add nofl * ci: make linter happy * Bump api version Co-authored-by: Aleksandr Kutuzov <[email protected]>
- Loading branch information
1 parent
57c438d
commit 8a95cb8
Showing
114 changed files
with
4,978 additions
and
931 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 4 additions & 0 deletions
4
applications/debug/unit_tests/resources/unit_tests/js/basic.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
let tests = require("tests"); | ||
|
||
tests.assert_eq(1337, 1337); | ||
tests.assert_eq("hello", "hello"); |
30 changes: 30 additions & 0 deletions
30
applications/debug/unit_tests/resources/unit_tests/js/event_loop.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
let tests = require("tests"); | ||
let event_loop = require("event_loop"); | ||
|
||
let ext = { | ||
i: 0, | ||
received: false, | ||
}; | ||
|
||
let queue = event_loop.queue(16); | ||
|
||
event_loop.subscribe(queue.input, function (_, item, tests, ext) { | ||
tests.assert_eq(123, item); | ||
ext.received = true; | ||
}, tests, ext); | ||
|
||
event_loop.subscribe(event_loop.timer("periodic", 1), function (_, _item, queue, counter, ext) { | ||
ext.i++; | ||
queue.send(123); | ||
if (counter === 10) | ||
event_loop.stop(); | ||
return [queue, counter + 1, ext]; | ||
}, queue, 1, ext); | ||
|
||
event_loop.subscribe(event_loop.timer("oneshot", 1000), function (_, _item, tests) { | ||
tests.fail("event loop was not stopped"); | ||
}, tests); | ||
|
||
event_loop.run(); | ||
tests.assert_eq(10, ext.i); | ||
tests.assert_eq(true, ext.received); |
34 changes: 34 additions & 0 deletions
34
applications/debug/unit_tests/resources/unit_tests/js/math.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
let tests = require("tests"); | ||
let math = require("math"); | ||
|
||
// math.EPSILON on Flipper Zero is 2.22044604925031308085e-16 | ||
|
||
// basics | ||
tests.assert_float_close(5, math.abs(-5), math.EPSILON); | ||
tests.assert_float_close(0.5, math.abs(-0.5), math.EPSILON); | ||
tests.assert_float_close(5, math.abs(5), math.EPSILON); | ||
tests.assert_float_close(0.5, math.abs(0.5), math.EPSILON); | ||
tests.assert_float_close(3, math.cbrt(27), math.EPSILON); | ||
tests.assert_float_close(6, math.ceil(5.3), math.EPSILON); | ||
tests.assert_float_close(31, math.clz32(1), math.EPSILON); | ||
tests.assert_float_close(5, math.floor(5.7), math.EPSILON); | ||
tests.assert_float_close(5, math.max(3, 5), math.EPSILON); | ||
tests.assert_float_close(3, math.min(3, 5), math.EPSILON); | ||
tests.assert_float_close(-1, math.sign(-5), math.EPSILON); | ||
tests.assert_float_close(5, math.trunc(5.7), math.EPSILON); | ||
|
||
// trig | ||
tests.assert_float_close(1.0471975511965976, math.acos(0.5), math.EPSILON); | ||
tests.assert_float_close(1.3169578969248166, math.acosh(2), math.EPSILON); | ||
tests.assert_float_close(0.5235987755982988, math.asin(0.5), math.EPSILON); | ||
tests.assert_float_close(1.4436354751788103, math.asinh(2), math.EPSILON); | ||
tests.assert_float_close(0.7853981633974483, math.atan(1), math.EPSILON); | ||
tests.assert_float_close(0.7853981633974483, math.atan2(1, 1), math.EPSILON); | ||
tests.assert_float_close(0.5493061443340549, math.atanh(0.5), math.EPSILON); | ||
tests.assert_float_close(-1, math.cos(math.PI), math.EPSILON * 18); // Error 3.77475828372553223744e-15 | ||
tests.assert_float_close(1, math.sin(math.PI / 2), math.EPSILON * 4.5); // Error 9.99200722162640886381e-16 | ||
|
||
// powers | ||
tests.assert_float_close(5, math.sqrt(25), math.EPSILON); | ||
tests.assert_float_close(8, math.pow(2, 3), math.EPSILON); | ||
tests.assert_float_close(2.718281828459045, math.exp(1), math.EPSILON * 2); // Error 4.44089209850062616169e-16 |
136 changes: 136 additions & 0 deletions
136
applications/debug/unit_tests/resources/unit_tests/js/storage.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
let storage = require("storage"); | ||
let tests = require("tests"); | ||
|
||
let baseDir = "/ext/.tmp/unit_tests"; | ||
|
||
tests.assert_eq(true, storage.rmrf(baseDir)); | ||
tests.assert_eq(true, storage.makeDirectory(baseDir)); | ||
|
||
// write | ||
let file = storage.openFile(baseDir + "/helloworld", "w", "create_always"); | ||
tests.assert_eq(true, !!file); | ||
tests.assert_eq(true, file.isOpen()); | ||
tests.assert_eq(13, file.write("Hello, World!")); | ||
tests.assert_eq(true, file.close()); | ||
tests.assert_eq(false, file.isOpen()); | ||
|
||
// read | ||
file = storage.openFile(baseDir + "/helloworld", "r", "open_existing"); | ||
tests.assert_eq(true, !!file); | ||
tests.assert_eq(true, file.isOpen()); | ||
tests.assert_eq(13, file.size()); | ||
tests.assert_eq("Hello, World!", file.read("ascii", 128)); | ||
tests.assert_eq(true, file.close()); | ||
tests.assert_eq(false, file.isOpen()); | ||
|
||
// seek | ||
file = storage.openFile(baseDir + "/helloworld", "r", "open_existing"); | ||
tests.assert_eq(true, !!file); | ||
tests.assert_eq(true, file.isOpen()); | ||
tests.assert_eq(13, file.size()); | ||
tests.assert_eq("Hello, World!", file.read("ascii", 128)); | ||
tests.assert_eq(true, file.seekAbsolute(1)); | ||
tests.assert_eq(true, file.seekRelative(2)); | ||
tests.assert_eq(3, file.tell()); | ||
tests.assert_eq(false, file.eof()); | ||
tests.assert_eq("lo, World!", file.read("ascii", 128)); | ||
tests.assert_eq(true, file.eof()); | ||
tests.assert_eq(true, file.close()); | ||
tests.assert_eq(false, file.isOpen()); | ||
|
||
// byte-level copy | ||
let src = storage.openFile(baseDir + "/helloworld", "r", "open_existing"); | ||
let dst = storage.openFile(baseDir + "/helloworld2", "rw", "create_always"); | ||
tests.assert_eq(true, !!src); | ||
tests.assert_eq(true, src.isOpen()); | ||
tests.assert_eq(true, !!dst); | ||
tests.assert_eq(true, dst.isOpen()); | ||
tests.assert_eq(true, src.copyTo(dst, 10)); | ||
tests.assert_eq(true, dst.seekAbsolute(0)); | ||
tests.assert_eq("Hello, Wor", dst.read("ascii", 128)); | ||
tests.assert_eq(true, src.copyTo(dst, 3)); | ||
tests.assert_eq(true, dst.seekAbsolute(0)); | ||
tests.assert_eq("Hello, World!", dst.read("ascii", 128)); | ||
tests.assert_eq(true, src.eof()); | ||
tests.assert_eq(true, src.close()); | ||
tests.assert_eq(false, src.isOpen()); | ||
tests.assert_eq(true, dst.eof()); | ||
tests.assert_eq(true, dst.close()); | ||
tests.assert_eq(false, dst.isOpen()); | ||
|
||
// truncate | ||
tests.assert_eq(true, storage.copy(baseDir + "/helloworld", baseDir + "/helloworld2")); | ||
file = storage.openFile(baseDir + "/helloworld2", "w", "open_existing"); | ||
tests.assert_eq(true, !!file); | ||
tests.assert_eq(true, file.seekAbsolute(5)); | ||
tests.assert_eq(true, file.truncate()); | ||
tests.assert_eq(true, file.close()); | ||
file = storage.openFile(baseDir + "/helloworld2", "r", "open_existing"); | ||
tests.assert_eq(true, !!file); | ||
tests.assert_eq("Hello", file.read("ascii", 128)); | ||
tests.assert_eq(true, file.close()); | ||
|
||
// existence | ||
tests.assert_eq(true, storage.fileExists(baseDir + "/helloworld")); | ||
tests.assert_eq(true, storage.fileExists(baseDir + "/helloworld2")); | ||
tests.assert_eq(false, storage.fileExists(baseDir + "/sus_amogus_123")); | ||
tests.assert_eq(false, storage.directoryExists(baseDir + "/helloworld")); | ||
tests.assert_eq(false, storage.fileExists(baseDir)); | ||
tests.assert_eq(true, storage.directoryExists(baseDir)); | ||
tests.assert_eq(true, storage.fileOrDirExists(baseDir)); | ||
tests.assert_eq(true, storage.remove(baseDir + "/helloworld2")); | ||
tests.assert_eq(false, storage.fileExists(baseDir + "/helloworld2")); | ||
|
||
// stat | ||
let stat = storage.stat(baseDir + "/helloworld"); | ||
tests.assert_eq(true, !!stat); | ||
tests.assert_eq(baseDir + "/helloworld", stat.path); | ||
tests.assert_eq(false, stat.isDirectory); | ||
tests.assert_eq(13, stat.size); | ||
|
||
// rename | ||
tests.assert_eq(true, storage.fileExists(baseDir + "/helloworld")); | ||
tests.assert_eq(false, storage.fileExists(baseDir + "/helloworld123")); | ||
tests.assert_eq(true, storage.rename(baseDir + "/helloworld", baseDir + "/helloworld123")); | ||
tests.assert_eq(false, storage.fileExists(baseDir + "/helloworld")); | ||
tests.assert_eq(true, storage.fileExists(baseDir + "/helloworld123")); | ||
tests.assert_eq(true, storage.rename(baseDir + "/helloworld123", baseDir + "/helloworld")); | ||
tests.assert_eq(true, storage.fileExists(baseDir + "/helloworld")); | ||
tests.assert_eq(false, storage.fileExists(baseDir + "/helloworld123")); | ||
|
||
// copy | ||
tests.assert_eq(true, storage.fileExists(baseDir + "/helloworld")); | ||
tests.assert_eq(false, storage.fileExists(baseDir + "/helloworld123")); | ||
tests.assert_eq(true, storage.copy(baseDir + "/helloworld", baseDir + "/helloworld123")); | ||
tests.assert_eq(true, storage.fileExists(baseDir + "/helloworld")); | ||
tests.assert_eq(true, storage.fileExists(baseDir + "/helloworld123")); | ||
|
||
// next avail | ||
tests.assert_eq("helloworld1", storage.nextAvailableFilename(baseDir, "helloworld", "", 20)); | ||
|
||
// fs info | ||
let fsInfo = storage.fsInfo("/ext"); | ||
tests.assert_eq(true, !!fsInfo); | ||
tests.assert_eq(true, fsInfo.freeSpace < fsInfo.totalSpace); // idk \(-_-)/ | ||
fsInfo = storage.fsInfo("/int"); | ||
tests.assert_eq(true, !!fsInfo); | ||
tests.assert_eq(true, fsInfo.freeSpace < fsInfo.totalSpace); | ||
|
||
// path operations | ||
tests.assert_eq(true, storage.arePathsEqual("/ext/test", "/ext/Test")); | ||
tests.assert_eq(false, storage.arePathsEqual("/ext/test", "/ext/Testttt")); | ||
tests.assert_eq(true, storage.isSubpathOf("/ext/test", "/ext/test/sub")); | ||
tests.assert_eq(false, storage.isSubpathOf("/ext/test/sub", "/ext/test")); | ||
|
||
// dir | ||
let entries = storage.readDirectory(baseDir); | ||
tests.assert_eq(true, !!entries); | ||
// FIXME: (-nofl) this test suite assumes that files are listed by | ||
// `readDirectory` in the exact order that they were created, which is not | ||
// something that is actually guaranteed. | ||
// Possible solution: sort and compare the array. | ||
tests.assert_eq("helloworld", entries[0].path); | ||
tests.assert_eq("helloworld123", entries[1].path); | ||
|
||
tests.assert_eq(true, storage.rmrf(baseDir)); | ||
tests.assert_eq(true, storage.makeDirectory(baseDir)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
#include "../test.h" // IWYU pragma: keep | ||
|
||
#include <furi.h> | ||
#include <furi_hal.h> | ||
#include <furi_hal_random.h> | ||
|
||
#include <storage/storage.h> | ||
#include <applications/system/js_app/js_thread.h> | ||
|
||
#include <stdint.h> | ||
|
||
#define JS_SCRIPT_PATH(name) EXT_PATH("unit_tests/js/" name ".js") | ||
|
||
typedef enum { | ||
JsTestsFinished = 1, | ||
JsTestsError = 2, | ||
} JsTestFlag; | ||
|
||
typedef struct { | ||
FuriEventFlag* event_flags; | ||
FuriString* error_string; | ||
} JsTestCallbackContext; | ||
|
||
static void js_test_callback(JsThreadEvent event, const char* msg, void* param) { | ||
JsTestCallbackContext* context = param; | ||
if(event == JsThreadEventPrint) { | ||
FURI_LOG_I("js_test", "%s", msg); | ||
} else if(event == JsThreadEventError || event == JsThreadEventErrorTrace) { | ||
context->error_string = furi_string_alloc_set_str(msg); | ||
furi_event_flag_set(context->event_flags, JsTestsFinished | JsTestsError); | ||
} else if(event == JsThreadEventDone) { | ||
furi_event_flag_set(context->event_flags, JsTestsFinished); | ||
} | ||
} | ||
|
||
static void js_test_run(const char* script_path) { | ||
JsTestCallbackContext* context = malloc(sizeof(JsTestCallbackContext)); | ||
context->event_flags = furi_event_flag_alloc(); | ||
|
||
JsThread* thread = js_thread_run(script_path, js_test_callback, context); | ||
uint32_t flags = furi_event_flag_wait( | ||
context->event_flags, JsTestsFinished, FuriFlagWaitAny, FuriWaitForever); | ||
if(flags & FuriFlagError) { | ||
// getting the flags themselves should not fail | ||
furi_crash(); | ||
} | ||
|
||
FuriString* error_string = context->error_string; | ||
|
||
js_thread_stop(thread); | ||
furi_event_flag_free(context->event_flags); | ||
free(context); | ||
|
||
if(flags & JsTestsError) { | ||
// memory leak: not freeing the FuriString if the tests fail, | ||
// because mu_fail executes a return | ||
// | ||
// who cares tho? | ||
mu_fail(furi_string_get_cstr(error_string)); | ||
} | ||
} | ||
|
||
MU_TEST(js_test_basic) { | ||
js_test_run(JS_SCRIPT_PATH("basic")); | ||
} | ||
MU_TEST(js_test_math) { | ||
js_test_run(JS_SCRIPT_PATH("math")); | ||
} | ||
MU_TEST(js_test_event_loop) { | ||
js_test_run(JS_SCRIPT_PATH("event_loop")); | ||
} | ||
MU_TEST(js_test_storage) { | ||
js_test_run(JS_SCRIPT_PATH("storage")); | ||
} | ||
|
||
MU_TEST_SUITE(test_js) { | ||
MU_RUN_TEST(js_test_basic); | ||
MU_RUN_TEST(js_test_math); | ||
MU_RUN_TEST(js_test_event_loop); | ||
MU_RUN_TEST(js_test_storage); | ||
} | ||
|
||
int run_minunit_test_js(void) { | ||
MU_RUN_SUITE(test_js); | ||
return MU_EXIT_CODE; | ||
} | ||
|
||
TEST_API_DEFINE(run_minunit_test_js) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.