mirror of
https://github.com/google/pebble.git
synced 2025-07-04 13:57:04 -04:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
29
tests/fw/javascript/test_jerry_port_common.h
Normal file
29
tests/fw/javascript/test_jerry_port_common.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "jmem-heap.h"
|
||||
|
||||
static int s_app_heap_analytics_log_stats_to_app_heartbeat_call_count;
|
||||
void app_heap_analytics_log_stats_to_app_heartbeat(bool is_rocky_app) {
|
||||
s_app_heap_analytics_log_stats_to_app_heartbeat_call_count++;
|
||||
}
|
||||
|
||||
static int s_app_heap_analytics_log_rocky_heap_oom_fault_call_count;
|
||||
void app_heap_analytics_log_rocky_heap_oom_fault(void) {
|
||||
s_app_heap_analytics_log_rocky_heap_oom_fault_call_count++;
|
||||
}
|
588
tests/fw/javascript/test_js.c
Normal file
588
tests/fw/javascript/test_js.c
Normal file
|
@ -0,0 +1,588 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
#include "test_jerry_port_common.h"
|
||||
#include "test_rocky_common.h"
|
||||
|
||||
#include "applib/graphics/gpath.h"
|
||||
#include "applib/preferred_content_size.h"
|
||||
#include "applib/rockyjs/api/rocky_api.h"
|
||||
#include "applib/rockyjs/api/rocky_api_global.h"
|
||||
#include "applib/rockyjs/api/rocky_api_timers.h"
|
||||
#include "applib/rockyjs/api/rocky_api_graphics.h"
|
||||
#include "applib/rockyjs/api/rocky_api_tickservice.h"
|
||||
#include "applib/rockyjs/api/rocky_api_util.h"
|
||||
#include "applib/rockyjs/pbl_jerry_port.h"
|
||||
|
||||
#include "syscall/syscall.h"
|
||||
|
||||
// Standard
|
||||
#include "string.h"
|
||||
#include "applib/rockyjs/rocky.h"
|
||||
|
||||
// Fakes
|
||||
#include "fake_app_timer.h"
|
||||
#include "fake_pbl_malloc.h"
|
||||
#include "fake_time.h"
|
||||
#include "fake_logging.h"
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_manager.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_sleep.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_sys_exit.h"
|
||||
|
||||
const RockyGlobalAPI APP_MESSAGE_APIS = {};
|
||||
const RockyGlobalAPI WATCHINFO_APIS = {};
|
||||
|
||||
size_t heap_bytes_free(void) {
|
||||
return 123456;
|
||||
}
|
||||
|
||||
void sys_analytics_inc(AnalyticsMetric metric, AnalyticsClient client) {
|
||||
}
|
||||
|
||||
bool sys_get_current_app_is_rocky_app(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void tick_timer_service_subscribe(TimeUnits tick_units, TickHandler handler) {}
|
||||
|
||||
static Window s_app_window_stack_get_top_window;
|
||||
Window *app_window_stack_get_top_window() {
|
||||
return &s_app_window_stack_get_top_window;
|
||||
}
|
||||
|
||||
PreferredContentSize preferred_content_size(void) {
|
||||
return PreferredContentSizeMedium;
|
||||
}
|
||||
|
||||
static MockCallRecordings s_layer_mark_dirty;
|
||||
void layer_mark_dirty(Layer *layer) {
|
||||
s_layer_mark_dirty.call_count++;
|
||||
s_layer_mark_dirty.last_call = (MockCallRecording){.layer = layer};
|
||||
}
|
||||
|
||||
static MockCallRecordings s_graphics_context_set_fill_color;
|
||||
void graphics_context_set_fill_color(GContext* ctx, GColor color) {
|
||||
s_graphics_context_set_fill_color.call_count++;
|
||||
s_graphics_context_set_fill_color.last_call = (MockCallRecording){.ctx = ctx, .color = color};
|
||||
}
|
||||
|
||||
static MockCallRecordings s_graphics_context_set_stroke_color;
|
||||
void graphics_context_set_stroke_color(GContext* ctx, GColor color) {
|
||||
s_graphics_context_set_stroke_color.call_count++;
|
||||
s_graphics_context_set_stroke_color.last_call = (MockCallRecording){.ctx = ctx, .color = color};
|
||||
}
|
||||
|
||||
static MockCallRecordings s_graphics_context_set_stroke_width;
|
||||
void graphics_context_set_stroke_width(GContext* ctx, uint8_t stroke_width) {
|
||||
s_graphics_context_set_stroke_width.call_count++;
|
||||
s_graphics_context_set_stroke_width.last_call =
|
||||
(MockCallRecording){.ctx = ctx, .width =stroke_width};
|
||||
}
|
||||
|
||||
|
||||
static MockCallRecordings s_graphics_line_draw_precise_stroked;
|
||||
void graphics_line_draw_precise_stroked(GContext* ctx, GPointPrecise p0, GPointPrecise p1) {
|
||||
record_mock_call(s_graphics_line_draw_precise_stroked) {
|
||||
.ctx = ctx, .pp0 = p0, .pp1 = p1,
|
||||
};
|
||||
}
|
||||
|
||||
static MockCallRecordings s_graphics_draw_line;
|
||||
void graphics_draw_line(GContext* ctx, GPoint p0, GPoint p1) {
|
||||
s_graphics_draw_line.call_count++;
|
||||
s_graphics_draw_line.last_call =
|
||||
(MockCallRecording){.ctx = ctx, .p0 = p0, .p1 = p1};
|
||||
}
|
||||
|
||||
static MockCallRecordings s_graphics_fill_rect;
|
||||
void graphics_fill_rect(GContext *ctx, const GRect *rect) {
|
||||
s_graphics_fill_rect.call_count++;
|
||||
s_graphics_fill_rect.last_call = (MockCallRecording){.ctx = ctx, .rect = *rect};
|
||||
}
|
||||
|
||||
static MockCallRecordings s_graphics_fill_rect;
|
||||
void graphics_fill_round_rect_by_value(GContext* ctx, GRect rect, uint16_t radius,
|
||||
GCornerMask corner_mask) {
|
||||
s_graphics_fill_rect.call_count++;
|
||||
s_graphics_fill_rect.last_call = (MockCallRecording) {
|
||||
.ctx = ctx,
|
||||
.rect = rect,
|
||||
.radius = radius,
|
||||
.corner_mask = corner_mask,
|
||||
};
|
||||
}
|
||||
|
||||
GPointPrecise gpoint_from_polar_precise(const GPointPrecise *precise_center,
|
||||
uint16_t precise_radius, int32_t angle) {
|
||||
return GPointPreciseFromGPoint(GPointZero);
|
||||
}
|
||||
|
||||
void graphics_draw_arc_precise_internal(GContext *ctx, GPointPrecise center, Fixed_S16_3 radius,
|
||||
int32_t angle_start, int32_t angle_end) {}
|
||||
|
||||
void graphics_draw_rect_precise(GContext *ctx, const GRectPrecise *rect) {}
|
||||
|
||||
void graphics_fill_radial_precise_internal(GContext *ctx, GPointPrecise center,
|
||||
Fixed_S16_3 radius_inner, Fixed_S16_3 radius_outer,
|
||||
int32_t angle_start, int32_t angle_end) {}
|
||||
|
||||
void gpath_draw_filled(GContext* ctx, GPath *path) {}
|
||||
|
||||
void layer_get_unobstructed_bounds(const Layer *layer, GRect *bounds_out) {
|
||||
*bounds_out = layer->bounds;
|
||||
}
|
||||
|
||||
GFont fonts_get_system_font(const char *font_key) {
|
||||
return (GFont)123;
|
||||
}
|
||||
|
||||
void graphics_draw_text(GContext *ctx, const char *text, GFont const font, const GRect box,
|
||||
const GTextOverflowMode overflow_mode, const GTextAlignment alignment,
|
||||
GTextAttributes *text_attributes) {}
|
||||
|
||||
void graphics_text_attributes_destroy(GTextAttributes *text_attributes) {}
|
||||
|
||||
GSize graphics_text_layout_get_max_used_size(GContext *ctx, const char *text,
|
||||
GFont const font, const GRect box,
|
||||
const GTextOverflowMode overflow_mode,
|
||||
const GTextAlignment alignment,
|
||||
GTextLayoutCacheRef layout) {
|
||||
return GSizeZero;
|
||||
}
|
||||
|
||||
uint32_t resource_storage_get_num_entries(ResAppNum app_num, uint32_t resource_id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool s_skip_mem_leak_check;
|
||||
|
||||
static void prv_init(void) {
|
||||
rocky_runtime_context_init();
|
||||
jerry_init(JERRY_INIT_EMPTY);
|
||||
}
|
||||
|
||||
static void prv_deinit(void) {
|
||||
jerry_cleanup();
|
||||
rocky_runtime_context_deinit();
|
||||
}
|
||||
|
||||
void test_js__initialize(void) {
|
||||
fake_pbl_malloc_clear_tracking();
|
||||
s_skip_mem_leak_check = false;
|
||||
|
||||
fake_app_timer_init();
|
||||
prv_init();
|
||||
s_app_window_stack_get_top_window = (Window){};
|
||||
s_app_state_get_graphics_context = NULL;
|
||||
|
||||
s_layer_mark_dirty = (MockCallRecordings){};
|
||||
s_graphics_context_set_fill_color = (MockCallRecordings){};
|
||||
s_graphics_context_set_stroke_color = (MockCallRecordings){};
|
||||
s_graphics_context_set_stroke_width = (MockCallRecordings){};
|
||||
s_graphics_line_draw_precise_stroked = (MockCallRecordings){};
|
||||
s_graphics_draw_line = (MockCallRecordings){};
|
||||
s_graphics_fill_rect = (MockCallRecordings){};
|
||||
|
||||
s_app_event_loop_callback = NULL;
|
||||
s_log_internal__expected = NULL;
|
||||
s_app_heap_analytics_log_stats_to_app_heartbeat_call_count = 0;
|
||||
s_app_heap_analytics_log_rocky_heap_oom_fault_call_count = 0;
|
||||
}
|
||||
|
||||
void test_js__cleanup(void) {
|
||||
fake_app_timer_deinit();
|
||||
s_log_internal__expected = NULL;
|
||||
|
||||
// some tests deinitialize the engine, avoid double de-init
|
||||
if (app_state_get_rocky_runtime_context() != NULL) {
|
||||
prv_deinit();
|
||||
}
|
||||
|
||||
// PBL-40702: test_js__init_deinit is leaking memory...
|
||||
if (!s_skip_mem_leak_check) {
|
||||
fake_pbl_malloc_check_net_allocs();
|
||||
}
|
||||
}
|
||||
|
||||
void test_js__addition(void) {
|
||||
char script[] = "var a = 1; var b = 2; var c = a + b;";
|
||||
EXECUTE_SCRIPT(script);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("c", 3.0);
|
||||
}
|
||||
|
||||
void test_js__eval_error(void) {
|
||||
prv_deinit(); // engine will be re-initialized in rocky_event_loop~
|
||||
char script[] = "function f({;";
|
||||
s_log_internal__expected = (const char *[]){
|
||||
"Not a snapshot, interpreting buffer as JS source code",
|
||||
"Exception while Evaluating JS",
|
||||
"SyntaxError: Identifier expected. [line: 1, column: 12]",
|
||||
NULL };
|
||||
rocky_event_loop_with_string_or_snapshot(script, sizeof(script));
|
||||
cl_assert(*s_log_internal__expected == NULL);
|
||||
}
|
||||
|
||||
AppTimer *rocky_timer_get_app_timer(void *data);
|
||||
|
||||
void test_js__init_deinit(void) {
|
||||
// PBL-40702: test_js__init_deinit is leaking memory...
|
||||
s_skip_mem_leak_check = true;
|
||||
|
||||
prv_deinit();
|
||||
|
||||
char *script =
|
||||
"var num_times = 0;"
|
||||
"var extra_arg = 0;"
|
||||
"var timer = setInterval(function(extra) {"
|
||||
"num_times++;"
|
||||
"extra_arg = extra;"
|
||||
"}, 1000, 5);";
|
||||
|
||||
for (int i = 0; i < 30; ++i) {
|
||||
prv_init();
|
||||
TIMER_APIS.init();
|
||||
EXECUTE_SCRIPT(script);
|
||||
prv_deinit();
|
||||
}
|
||||
|
||||
prv_init();
|
||||
}
|
||||
|
||||
static char *prv_load_js(char *suffix) {
|
||||
char path[512] = {0};
|
||||
snprintf(path, sizeof(path), "%s/js/tictoc~rect~%s.js", CLAR_FIXTURE_PATH, suffix);
|
||||
FILE *f = fopen(path, "r");
|
||||
cl_assert(f);
|
||||
fseek(f, 0, SEEK_END);
|
||||
size_t length = (size_t)ftell(f);
|
||||
fseek (f, 0, SEEK_SET);
|
||||
char *buffer = malloc(length + 1);
|
||||
memset(buffer, 0, length + 1);
|
||||
cl_assert(buffer);
|
||||
fread (buffer, 1, length, f);
|
||||
fclose (f);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void test_js__call_cleanup_twice(void) {
|
||||
prv_deinit();
|
||||
char *script = "function f(i) { return i * 4; } f(5);";
|
||||
bool result = rocky_event_loop_with_string_or_snapshot(script, strlen(script));
|
||||
cl_assert(result);
|
||||
}
|
||||
|
||||
static bool s_tictoc_callback_is_color;
|
||||
static void prv_rocky_tictoc_callback(void) {
|
||||
Layer *root_layer = &s_app_window_stack_get_top_window.layer;
|
||||
root_layer->bounds = GRect(10, 20, 30, 40);
|
||||
cl_assert(root_layer->update_proc);
|
||||
GContext ctx = {.lock = true};
|
||||
root_layer->update_proc(root_layer, &ctx);
|
||||
|
||||
if (s_tictoc_callback_is_color) {
|
||||
cl_assert_equal_i(1, s_graphics_fill_rect.call_count);
|
||||
cl_assert_equal_i(4, s_graphics_line_draw_precise_stroked.call_count);
|
||||
cl_assert_equal_i(0, s_graphics_draw_line.call_count);
|
||||
cl_assert_equal_i(1, s_graphics_context_set_fill_color.call_count);
|
||||
cl_assert_equal_i(4, s_graphics_context_set_stroke_color.call_count);
|
||||
cl_assert_equal_i(4, s_graphics_context_set_stroke_width.call_count);
|
||||
} else {
|
||||
cl_assert_equal_i(2, s_graphics_fill_rect.call_count);
|
||||
cl_assert_equal_i(0, s_graphics_line_draw_precise_stroked.call_count);
|
||||
cl_assert_equal_i(0, s_graphics_draw_line.call_count);
|
||||
cl_assert_equal_i(1, s_graphics_context_set_fill_color.call_count);
|
||||
cl_assert_equal_i(0, s_graphics_context_set_stroke_color.call_count);
|
||||
cl_assert_equal_i(0, s_graphics_context_set_stroke_width.call_count);
|
||||
}
|
||||
|
||||
// run update proc multiple times to verify we don't have a memory leak
|
||||
for (int i = 1024; i >=0; i--) {
|
||||
root_layer->update_proc(root_layer, &ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void test_js__rocky_tictoc_color(void) {
|
||||
prv_deinit(); // engine will be re-initialized in rocky_event_loop~
|
||||
char *script = prv_load_js("color");
|
||||
s_tictoc_callback_is_color = true;
|
||||
s_app_event_loop_callback = prv_rocky_tictoc_callback;
|
||||
bool result = rocky_event_loop_with_string_or_snapshot(script, strlen(script));
|
||||
cl_assert(result);
|
||||
}
|
||||
|
||||
void test_js__rocky_tictoc_bw(void) {
|
||||
GContext ctx = {};
|
||||
s_app_state_get_graphics_context = &ctx;
|
||||
prv_deinit(); // engine will be re-initialized in rocky_event_loop~
|
||||
char *script = prv_load_js("bw");
|
||||
s_tictoc_callback_is_color = false;
|
||||
s_app_event_loop_callback = prv_rocky_tictoc_callback;
|
||||
bool result = rocky_event_loop_with_string_or_snapshot(script, strlen(script));
|
||||
cl_assert(result);
|
||||
}
|
||||
|
||||
void test_js__recursion(void) {
|
||||
const char script[] =
|
||||
"function f(i) { \n"
|
||||
" if (i == 0) {_rocky.requestDraw();} \n"
|
||||
" else {f(i-1)}\n"
|
||||
"}\n"
|
||||
"f(10)";
|
||||
static const RockyGlobalAPI *apis[] = {
|
||||
&GRAPHIC_APIS,
|
||||
NULL,
|
||||
};
|
||||
rocky_global_init(apis);
|
||||
EXECUTE_SCRIPT(script);
|
||||
|
||||
cl_assert_equal_i(1, s_layer_mark_dirty.call_count);
|
||||
}
|
||||
|
||||
void test_js__no_print_builtin(void) {
|
||||
JS_VAR global_obj = jerry_get_global_object();
|
||||
JS_VAR print_builtin = jerry_get_object_field(global_obj, "print");
|
||||
cl_assert_equal_b(true, jerry_value_is_undefined(print_builtin));
|
||||
}
|
||||
|
||||
void test_js__sin_cos(void) {
|
||||
EXECUTE_SCRIPT(
|
||||
"var s1 = 100 + 50 * Math.sin(0);\n"
|
||||
"var s2 = 100 + 50 * Math.sin(2 * Math.PI);\n"
|
||||
"var c1 = 100 + 50 * Math.cos(0);\n"
|
||||
"var c2 = 100 + 50 * Math.cos(2 * Math.PI);\n"
|
||||
);
|
||||
cl_assert_equal_i(100, (int32_t)jerry_get_number_value(prv_js_global_get_value("s1")));
|
||||
cl_assert_equal_i(99, (int32_t)jerry_get_number_value(prv_js_global_get_value("s2")));
|
||||
cl_assert_equal_i(150, (int32_t)jerry_get_number_value(prv_js_global_get_value("c1")));
|
||||
cl_assert_equal_i(150, (int32_t)jerry_get_number_value(prv_js_global_get_value("c2")));
|
||||
|
||||
cl_assert_equal_i(100, jerry_get_int32_value(prv_js_global_get_value("s1")));
|
||||
cl_assert_equal_i(100, jerry_get_int32_value(prv_js_global_get_value("s2")));
|
||||
cl_assert_equal_i(150, jerry_get_int32_value(prv_js_global_get_value("c1")));
|
||||
cl_assert_equal_i(150, jerry_get_int32_value(prv_js_global_get_value("c2")));
|
||||
}
|
||||
|
||||
void test_js__date(void) {
|
||||
const time_t cur_time = 1458250851; // Thu Mar 17 21:40:51 2016 UTC
|
||||
// Thu Mar 17 14:40:51 2016 PDT
|
||||
const uint16_t cur_millis = 123;
|
||||
fake_time_init(cur_time, cur_millis);
|
||||
fake_time_set_gmtoff(-8 * 60 * 60); // PST
|
||||
fake_time_set_dst(1 * 60 * 60, 1458111600, 1465628400); // PDT 3/16 -> 11/6 2016
|
||||
|
||||
char *script =
|
||||
"var date_now = new Date();"
|
||||
"var now = date_now.getTime();"
|
||||
"var local_day = date_now.getDay();"
|
||||
"var local_hour = date_now.getHours();";
|
||||
EXECUTE_SCRIPT(script);
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_D("now", (double)cur_time * 1000.0 + (double)cur_millis);
|
||||
ASSERT_JS_GLOBAL_EQUALS_D("local_day", 4.0); // Thursday
|
||||
ASSERT_JS_GLOBAL_EQUALS_D("local_hour", 14.0); // 1pm
|
||||
}
|
||||
|
||||
void test_js__log_exception(void) {
|
||||
char *script =
|
||||
"var e1;\n"
|
||||
"var f1 = function(){throw new Error('test')};\n"
|
||||
"var f2 = function(){throw new 'test';};\n"
|
||||
"var f2 = function(){throw new 123;};\n"
|
||||
"try {f1();} catch(e) {e1 = e;}\n"
|
||||
"try {f2();} catch(e) {e2 = e;}\n"
|
||||
"try {f3();} catch(e) {e3 = e;}\n";
|
||||
|
||||
EXECUTE_SCRIPT(script);
|
||||
jerry_value_t e1 = prv_js_global_get_value("e1");
|
||||
jerry_value_t e2 = prv_js_global_get_value("e2");
|
||||
jerry_value_t e3 = prv_js_global_get_value("e3");
|
||||
|
||||
// error
|
||||
s_log_internal__expected = (const char *[]){
|
||||
"Exception while e1", "Error: test", NULL,
|
||||
};
|
||||
rocky_log_exception("e1", e1);
|
||||
cl_assert(*s_log_internal__expected == NULL);
|
||||
|
||||
// string
|
||||
s_log_internal__expected = (const char *[]){
|
||||
"Exception while e2", "TypeError", NULL,
|
||||
};
|
||||
rocky_log_exception("e2", e2);
|
||||
cl_assert(*s_log_internal__expected == NULL);
|
||||
|
||||
// number
|
||||
s_log_internal__expected = (const char *[]){
|
||||
"Exception while e3", "ReferenceError", NULL,
|
||||
};
|
||||
rocky_log_exception("e3", e3);
|
||||
cl_assert(*s_log_internal__expected == NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: JS Tests should be built in a 32-bit env
|
||||
void test_js__size(void) {
|
||||
cl_assert_equal_i(4, sizeof(size_t));
|
||||
}
|
||||
*/
|
||||
|
||||
void test_js__snapshot(void) {
|
||||
prv_deinit();
|
||||
rocky_runtime_context_init();
|
||||
jerry_init(JERRY_INIT_SHOW_OPCODES);
|
||||
char *const script = prv_load_js("color");
|
||||
uint8_t snapshot[65536] = { 0 };
|
||||
|
||||
// make sure snapshot data starts with expected Rocky header
|
||||
const size_t header_size = sizeof(ROCKY_EXPECTED_SNAPSHOT_HEADER);
|
||||
cl_assert_equal_i(8, header_size);
|
||||
// NOTE: the snapshot header in this unit test is fixed to
|
||||
// CAPABILITY_JAVASCRIPT_BYTECODE_VERSION=1 only use the resulting binary
|
||||
// if the true JS version matches
|
||||
memcpy(snapshot, &ROCKY_EXPECTED_SNAPSHOT_HEADER, header_size);
|
||||
const size_t snapshot_size = jerry_parse_and_save_snapshot((const jerry_char_t *)script,
|
||||
strlen(script),
|
||||
true, /* is_for_global */
|
||||
false, /* is_strict */
|
||||
snapshot + header_size,
|
||||
sizeof(snapshot) - header_size);
|
||||
cl_assert(snapshot_size > 512); // make sure it contains "something" and compiling didn't fail
|
||||
|
||||
prv_deinit();
|
||||
|
||||
bool result = rocky_event_loop_with_string_or_snapshot(snapshot, snapshot_size);
|
||||
cl_assert(result);
|
||||
}
|
||||
|
||||
static int s_cleanup_calls;
|
||||
static void prv_cleanup_cb(const uintptr_t native_p) {
|
||||
++s_cleanup_calls;
|
||||
}
|
||||
|
||||
void test_js__js_value_cleanup(void) {
|
||||
s_cleanup_calls = 0;
|
||||
|
||||
{
|
||||
// Sanity check:
|
||||
// we don't clean up when a bare jerry_value_t goes out of scope.
|
||||
jerry_value_t value = jerry_create_object();
|
||||
jerry_set_object_native_handle(value, 0, prv_cleanup_cb);
|
||||
}
|
||||
jerry_gc(); // Perform GC in case refcount = 0
|
||||
cl_assert_equal_i(s_cleanup_calls, 0); // Never release()d, so wasn't cleaned.
|
||||
|
||||
{
|
||||
// When this goes out of scope, we do clean up
|
||||
JS_VAR value = jerry_create_object();
|
||||
jerry_set_object_native_handle(value, 0, prv_cleanup_cb);
|
||||
}
|
||||
jerry_gc(); // Perform GC so it will be cleaned up if refcount = 0
|
||||
cl_assert_equal_i(s_cleanup_calls, 1); // Make sure that it was cleaned up
|
||||
|
||||
{
|
||||
// Create a regular value, attach the native handle
|
||||
jerry_value_t value = jerry_create_object();
|
||||
jerry_set_object_native_handle(value, 0, prv_cleanup_cb);
|
||||
|
||||
// Create an autoreleased variable that points to the same, it will be cleaned up.
|
||||
JS_UNUSED_VAL = value;
|
||||
}
|
||||
jerry_gc();
|
||||
cl_assert_equal_i(s_cleanup_calls, 2);
|
||||
|
||||
{
|
||||
// Naming check on unused variables, shouldn't clash.
|
||||
// This is really just a compile-time test.
|
||||
JS_UNUSED_VAL = jerry_create_object();
|
||||
JS_UNUSED_VAL = jerry_create_object();
|
||||
}
|
||||
}
|
||||
|
||||
void test_js__get_global_builtin(void) {
|
||||
jerry_value_t date_builtin = jerry_get_global_builtin((const jerry_char_t *)"Date");
|
||||
cl_assert(!jerry_value_is_undefined(date_builtin));
|
||||
cl_assert(jerry_value_is_constructor(date_builtin));
|
||||
jerry_release_value(date_builtin);
|
||||
|
||||
jerry_value_t json_builtin = jerry_get_global_builtin((const jerry_char_t *)"JSON");
|
||||
cl_assert(jerry_value_is_object(json_builtin));
|
||||
jerry_release_value(json_builtin);
|
||||
|
||||
jerry_value_t not_builtin = jerry_get_global_builtin((const jerry_char_t *)"_not_builtin_");
|
||||
cl_assert(jerry_value_is_undefined(not_builtin));
|
||||
}
|
||||
|
||||
void test_js__get_global_builtin_compare(void) {
|
||||
jerry_value_t date_builtin = jerry_get_global_builtin((const jerry_char_t *)"Date");
|
||||
jerry_value_t global_object = jerry_get_global_object();
|
||||
|
||||
// Compare that the global Date is the same object as the builtin
|
||||
jerry_value_t global_date = jerry_get_object_field(global_object, "Date");
|
||||
cl_assert(date_builtin == global_date);
|
||||
|
||||
jerry_release_value(global_date);
|
||||
jerry_release_value(global_object);
|
||||
jerry_release_value(date_builtin);
|
||||
}
|
||||
|
||||
void test_js__get_global_builtin_changed(void) {
|
||||
jerry_value_t date_builtin = jerry_get_global_builtin((const jerry_char_t *)"Date");
|
||||
jerry_value_t global_object = jerry_get_global_object();
|
||||
|
||||
const char *source = "Date = 'some string';";
|
||||
jerry_eval((const jerry_char_t *)source, strlen(source), false);
|
||||
|
||||
// After changing the global date object, it should not match our builtin
|
||||
jerry_value_t global_date = jerry_get_object_field(global_object, "Date");
|
||||
cl_assert(jerry_value_is_string(global_date));
|
||||
cl_assert(date_builtin != global_date);
|
||||
|
||||
jerry_release_value(global_date);
|
||||
jerry_release_value(global_object);
|
||||
jerry_release_value(date_builtin);
|
||||
}
|
||||
|
||||
void test_js__capture_mem_stats_upon_exiting_event_loop(void) {
|
||||
prv_deinit();
|
||||
|
||||
s_app_event_loop_callback = NULL;
|
||||
const char *source = ";";
|
||||
cl_assert_equal_b(true, rocky_event_loop_with_string_or_snapshot(source, strlen(source)));
|
||||
cl_assert_equal_i(s_app_heap_analytics_log_stats_to_app_heartbeat_call_count, 1);
|
||||
}
|
||||
|
||||
void test_js__jmem_heap_stats_largest_free_block_bytes(void) {
|
||||
jmem_heap_stats_t stats = {};
|
||||
jmem_heap_get_stats(&stats);
|
||||
// Note: this might fail in the future if JerryScript would happen to cause fragmentation right
|
||||
// upon initializing the engine:
|
||||
cl_assert_equal_i(stats.size - stats.allocated_bytes, stats.largest_free_block_bytes);
|
||||
}
|
||||
|
||||
void test_js__capture_jerry_heap_oom_stats(void) {
|
||||
const char *source = "var big = []; for (;;) { big += 'bigger'; };";
|
||||
cl_assert_passert(jerry_eval((const jerry_char_t *)source, strlen(source), false));
|
||||
cl_assert_equal_i(s_app_heap_analytics_log_rocky_heap_oom_fault_call_count, 1);
|
||||
}
|
971
tests/fw/javascript/test_rocky_api_app_message.c
Normal file
971
tests/fw/javascript/test_rocky_api_app_message.c
Normal file
|
@ -0,0 +1,971 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
#include "test_jerry_port_common.h"
|
||||
#include "test_rocky_common.h"
|
||||
|
||||
#include "applib/rockyjs/api/rocky_api_global.h"
|
||||
#include "applib/rockyjs/api/rocky_api_app_message.h"
|
||||
#include "applib/rockyjs/pbl_jerry_port.h"
|
||||
|
||||
#include "applib/app_message/app_message.h"
|
||||
#include "util/dict.h"
|
||||
#include "util/size.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// Fakes
|
||||
#include "fake_app_timer.h"
|
||||
#include "fake_event_service.h"
|
||||
#include "fake_pbl_malloc.h"
|
||||
#include "fake_time.h"
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_comm_session.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_sys_exit.h"
|
||||
|
||||
extern PostMessageState rocky_api_app_message_get_state(void);
|
||||
extern AppTimer *rocky_api_app_message_get_app_msg_retry_timer(void);
|
||||
extern AppTimer *rocky_api_app_message_get_session_closed_object_queue_timer(void);
|
||||
|
||||
T_STATIC jerry_value_t prv_json_stringify(jerry_value_t object);
|
||||
T_STATIC jerry_value_t prv_json_parse(const char *);
|
||||
|
||||
T_STATIC void prv_handle_connection(void);
|
||||
T_STATIC void prv_handle_disconnection(void);
|
||||
|
||||
// App message mocks
|
||||
|
||||
static AppMessageInboxReceived s_received_callback;
|
||||
AppMessageInboxReceived app_message_register_inbox_received(
|
||||
AppMessageInboxReceived received_callback) {
|
||||
AppMessageInboxReceived prev_cb = s_received_callback;
|
||||
s_received_callback = received_callback;
|
||||
return prev_cb;
|
||||
}
|
||||
|
||||
static AppMessageInboxDropped s_dropped_callback;
|
||||
AppMessageInboxDropped app_message_register_inbox_dropped(AppMessageInboxDropped dropped_callback) {
|
||||
AppMessageInboxDropped prev_cb = s_dropped_callback;
|
||||
s_dropped_callback = dropped_callback;
|
||||
return prev_cb;
|
||||
}
|
||||
|
||||
static AppMessageOutboxSent s_sent_callback;
|
||||
AppMessageOutboxSent app_message_register_outbox_sent(AppMessageOutboxSent sent_callback) {
|
||||
AppMessageOutboxSent prev_cb = s_sent_callback;
|
||||
s_sent_callback = sent_callback;
|
||||
return prev_cb;
|
||||
}
|
||||
|
||||
static AppMessageOutboxFailed s_failed_callback;
|
||||
AppMessageOutboxFailed app_message_register_outbox_failed(AppMessageOutboxFailed failed_callback) {
|
||||
AppMessageOutboxFailed prev_cb = s_failed_callback;
|
||||
s_failed_callback = failed_callback;
|
||||
return prev_cb;
|
||||
}
|
||||
|
||||
void app_message_deregister_callbacks(void) {
|
||||
s_received_callback = NULL;
|
||||
s_dropped_callback = NULL;
|
||||
s_sent_callback = NULL;
|
||||
s_failed_callback = NULL;
|
||||
}
|
||||
|
||||
static uint32_t s_inbox_size;
|
||||
static uint32_t s_outbox_size;
|
||||
AppMessageResult app_message_open(const uint32_t size_inbound, const uint32_t size_outbound) {
|
||||
s_inbox_size = size_inbound;
|
||||
s_outbox_size = size_outbound;
|
||||
return APP_MSG_OK;
|
||||
}
|
||||
|
||||
static bool s_is_outbox_message_pending;
|
||||
static DictionaryIterator s_outbox_iterator;
|
||||
static uint8_t *s_outbox_buffer;
|
||||
AppMessageResult app_message_outbox_begin(DictionaryIterator **iterator) {
|
||||
cl_assert_equal_b(s_is_outbox_message_pending, false);
|
||||
if (!s_outbox_buffer) {
|
||||
s_outbox_buffer = malloc(s_outbox_size);
|
||||
}
|
||||
dict_write_begin(&s_outbox_iterator, s_outbox_buffer, s_outbox_size);
|
||||
*iterator = &s_outbox_iterator;
|
||||
|
||||
return APP_MSG_OK;
|
||||
}
|
||||
|
||||
static int s_app_message_outbox_send_call_count;
|
||||
AppMessageResult app_message_outbox_send(void) {
|
||||
++s_app_message_outbox_send_call_count;
|
||||
s_is_outbox_message_pending = true;
|
||||
return APP_MSG_OK;
|
||||
}
|
||||
|
||||
static bool s_comm_session_connected;
|
||||
CommSession *sys_app_pp_get_comm_session(void) {
|
||||
return (CommSession *)s_comm_session_connected;
|
||||
}
|
||||
|
||||
static void prv_rcv_app_message_ack(AppMessageResult result) {
|
||||
void *context = NULL;
|
||||
cl_assert_equal_b(s_is_outbox_message_pending, true);
|
||||
s_is_outbox_message_pending = false;
|
||||
if (result == APP_MSG_OK) {
|
||||
s_sent_callback(&s_outbox_iterator, context);
|
||||
} else {
|
||||
s_failed_callback(&s_outbox_iterator, result, context);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_app_message_setup(void) {
|
||||
s_inbox_size = 0;
|
||||
s_outbox_size = 0;
|
||||
s_outbox_buffer = NULL;
|
||||
s_app_message_outbox_send_call_count = 0;
|
||||
s_is_outbox_message_pending = false;
|
||||
app_message_deregister_callbacks();
|
||||
}
|
||||
|
||||
static void prv_app_message_teardown(void) {
|
||||
if (s_outbox_buffer) {
|
||||
free(s_outbox_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// Statics and Utilities
|
||||
|
||||
static void prv_init_and_goto_session_open(void);
|
||||
|
||||
static void prv_simulate_transport_connection_event(bool is_connected) {
|
||||
// FIXME: use events here instead of poking at the internals!
|
||||
if (is_connected) {
|
||||
prv_handle_connection();
|
||||
} else {
|
||||
prv_handle_disconnection();
|
||||
}
|
||||
}
|
||||
|
||||
static const RockyGlobalAPI *s_app_message_api[] = {
|
||||
&APP_MESSAGE_APIS,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void prv_init_api(bool start_connected) {
|
||||
s_comm_session_connected = start_connected;
|
||||
rocky_global_init(s_app_message_api);
|
||||
}
|
||||
|
||||
// Setup
|
||||
|
||||
void test_rocky_api_app_message__initialize(void) {
|
||||
fake_app_timer_init();
|
||||
fake_pbl_malloc_clear_tracking();
|
||||
prv_app_message_setup();
|
||||
|
||||
s_process_manager_callback = NULL;
|
||||
s_process_manager_callback_data = NULL;
|
||||
|
||||
rocky_runtime_context_init();
|
||||
jerry_init(JERRY_INIT_EMPTY);
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__cleanup(void) {
|
||||
rocky_global_deinit();
|
||||
jerry_cleanup();
|
||||
rocky_runtime_context_deinit();
|
||||
prv_app_message_teardown();
|
||||
fake_pbl_malloc_check_net_allocs();
|
||||
fake_app_timer_deinit();
|
||||
}
|
||||
|
||||
static const PostMessageResetCompletePayload VALID_RESET_COMPLETE = {
|
||||
.min_supported_version = POSTMESSAGE_PROTOCOL_MIN_VERSION,
|
||||
.max_supported_version = POSTMESSAGE_PROTOCOL_MAX_VERSION,
|
||||
.max_tx_chunk_size = POSTMESSAGE_PROTOCOL_MAX_TX_CHUNK_SIZE,
|
||||
.max_rx_chunk_size = POSTMESSAGE_PROTOCOL_MAX_RX_CHUNK_SIZE,
|
||||
};
|
||||
|
||||
static const size_t TINY_CHUNK_SIZE = 4;
|
||||
|
||||
static const PostMessageResetCompletePayload TINY_RESET_COMPLETE = {
|
||||
.min_supported_version = POSTMESSAGE_PROTOCOL_MIN_VERSION,
|
||||
.max_supported_version = POSTMESSAGE_PROTOCOL_MAX_VERSION,
|
||||
.max_tx_chunk_size = TINY_CHUNK_SIZE,
|
||||
.max_rx_chunk_size = TINY_CHUNK_SIZE,
|
||||
};
|
||||
|
||||
#define RCV_APP_MESSAGE(...) \
|
||||
do { \
|
||||
Tuplet tuplets[] = { __VA_ARGS__ }; \
|
||||
uint32_t buffer_size = dict_calc_buffer_size_from_tuplets(tuplets, ARRAY_LENGTH(tuplets)); \
|
||||
uint8_t buffer[buffer_size]; \
|
||||
DictionaryIterator it; \
|
||||
const DictionaryResult result = \
|
||||
dict_serialize_tuplets_to_buffer_with_iter(&it, tuplets, ARRAY_LENGTH(tuplets), \
|
||||
buffer, &buffer_size); \
|
||||
cl_assert_equal_i(DICT_OK, result); \
|
||||
if (s_received_callback) { \
|
||||
s_received_callback(&it, NULL); \
|
||||
} \
|
||||
} while(0);
|
||||
|
||||
|
||||
#define RCV_RESET_REQUEST() \
|
||||
RCV_APP_MESSAGE(TupletBytes(PostMessageKeyResetRequest, NULL, 0));
|
||||
|
||||
#define RCV_RESET_COMPLETE() \
|
||||
RCV_APP_MESSAGE(TupletBytes(PostMessageKeyResetComplete, \
|
||||
(const uint8_t *)&VALID_RESET_COMPLETE, sizeof(VALID_RESET_COMPLETE)));
|
||||
|
||||
#define RCV_DUMMY_CHUNK() \
|
||||
do { \
|
||||
PostMessageChunkPayload chunk = {}; \
|
||||
RCV_APP_MESSAGE(TupletBytes(PostMessageKeyChunk, (const uint8_t *) &chunk, sizeof(chunk))); \
|
||||
} while(0);
|
||||
|
||||
//! Asserts whether the outbox has a pending message containing the tuples passed to this macro.
|
||||
//! The value and type of the tuples is also asserted.
|
||||
//! @note Only asserts if expected tuples are MISSING. It will not trip if there are other
|
||||
//! (non-expected) tuples in the set.
|
||||
#define EXPECT_OUTBOX_MESSAGE_PENDING(...) \
|
||||
do { \
|
||||
cl_assert_equal_b(true, s_is_outbox_message_pending); \
|
||||
/* The cursor must be updated! */ \
|
||||
cl_assert(s_outbox_iterator.cursor != s_outbox_iterator.dictionary->head); \
|
||||
Tuplet tuplets[] = { __VA_ARGS__ }; \
|
||||
uint32_t buffer_size = dict_calc_buffer_size_from_tuplets(tuplets, ARRAY_LENGTH(tuplets)); \
|
||||
uint8_t buffer[buffer_size]; \
|
||||
DictionaryIterator expected_it; \
|
||||
const DictionaryResult result = \
|
||||
dict_serialize_tuplets_to_buffer_with_iter(&expected_it, tuplets, ARRAY_LENGTH(tuplets), \
|
||||
buffer, &buffer_size); \
|
||||
cl_assert_equal_i(DICT_OK, result); \
|
||||
for (Tuple *expected_t = dict_read_first(&expected_it); expected_t != NULL; \
|
||||
expected_t = dict_read_next(&expected_it)) { \
|
||||
Tuple *found_t = dict_find(&s_outbox_iterator, expected_t->key); \
|
||||
cl_assert(found_t); \
|
||||
cl_assert_equal_i(found_t->type, expected_t->type); \
|
||||
cl_assert_equal_i(found_t->length, expected_t->length); \
|
||||
if (expected_t->length) { \
|
||||
cl_assert_equal_i(0, memcmp(found_t->value[0].data, expected_t->value[0].data, \
|
||||
expected_t->length)); \
|
||||
} \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
#define EXPECT_OUTBOX_NO_MESSAGE_PENDING() \
|
||||
cl_assert_equal_b(false, s_is_outbox_message_pending);
|
||||
|
||||
#define EXPECT_OUTBOX_RESET_REQUEST() \
|
||||
EXPECT_OUTBOX_MESSAGE_PENDING(TupletBytes(PostMessageKeyResetRequest, NULL, 0));
|
||||
|
||||
#define EXPECT_OUTBOX_RESET_COMPLETE_PENDING() \
|
||||
EXPECT_OUTBOX_MESSAGE_PENDING(TupletBytes(PostMessageKeyResetComplete, \
|
||||
(const uint8_t *) &VALID_RESET_COMPLETE, \
|
||||
sizeof(VALID_RESET_COMPLETE)));
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Negotiation Steps
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void test_rocky_api_app_message__disconnected__ignore_any_app_message(void) {
|
||||
prv_init_api(false /* start_connected */);
|
||||
|
||||
for (PostMessageKey key = PostMessageKeyResetRequest; key < PostMessageKey_Count; ++key) {
|
||||
uint8_t dummy_data[] = {0, 1, 2};
|
||||
RCV_APP_MESSAGE(TupletBytes(key, dummy_data, sizeof(dummy_data)));
|
||||
}
|
||||
|
||||
cl_assert_equal_i(0, s_app_message_outbox_send_call_count);
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateDisconnected);
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__awaiting_reset_request__receive_reset_request(void) {
|
||||
prv_init_api(true /* start_connected */);
|
||||
|
||||
RCV_RESET_REQUEST();
|
||||
|
||||
// Expect responding with a ResetComplete:
|
||||
EXPECT_OUTBOX_RESET_COMPLETE_PENDING();
|
||||
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(),
|
||||
PostMessageStateAwaitingResetCompleteRemoteInitiated);
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__awaiting_reset_request__receive_chunk(void) {
|
||||
prv_init_api(false /* start_connected */);
|
||||
prv_simulate_transport_connection_event(true /* is_connected */);
|
||||
|
||||
RCV_DUMMY_CHUNK();
|
||||
// https://pebbletechnology.atlassian.net/browse/PBL-42466
|
||||
// TODO: assert that app message was NACK'd
|
||||
|
||||
// Expect responding with a ResetRequest:
|
||||
EXPECT_OUTBOX_RESET_REQUEST();
|
||||
EXPECT_OUTBOX_MESSAGE_PENDING(TupletBytes(PostMessageKeyResetRequest, NULL, 0));
|
||||
// TODO: check fields
|
||||
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(),
|
||||
PostMessageStateAwaitingResetCompleteLocalInitiated);
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__awaiting_reset_request__disconnect(void) {
|
||||
prv_init_api(false /* start_connected */);
|
||||
prv_simulate_transport_connection_event(true /* is_connected */);
|
||||
prv_simulate_transport_connection_event(false /* is_connected */);
|
||||
EXPECT_OUTBOX_NO_MESSAGE_PENDING();
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateDisconnected);
|
||||
}
|
||||
|
||||
static void prv_init_and_goto_awaiting_reset_complete_remote_initiated(void) {
|
||||
prv_init_api(true /* start_connected */);
|
||||
|
||||
RCV_RESET_REQUEST();
|
||||
|
||||
EXPECT_OUTBOX_RESET_COMPLETE_PENDING();
|
||||
prv_rcv_app_message_ack(APP_MSG_OK);
|
||||
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(),
|
||||
PostMessageStateAwaitingResetCompleteRemoteInitiated);
|
||||
}
|
||||
|
||||
static void prv_init_and_goto_awaiting_reset_complete_local_initiated(void) {
|
||||
prv_init_and_goto_awaiting_reset_complete_remote_initiated();
|
||||
RCV_DUMMY_CHUNK();
|
||||
EXPECT_OUTBOX_RESET_REQUEST();
|
||||
prv_rcv_app_message_ack(APP_MSG_OK);
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(),
|
||||
PostMessageStateAwaitingResetCompleteLocalInitiated);
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__awaiting_reset_complete_rem_init__receive_complete_valid_version(void) {
|
||||
prv_init_and_goto_awaiting_reset_complete_remote_initiated();
|
||||
|
||||
RCV_RESET_COMPLETE();
|
||||
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateSessionOpen);
|
||||
EXPECT_OUTBOX_NO_MESSAGE_PENDING();
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__awaiting_reset_complete_rem_init__receive_complete_unsupported_ver(void) {
|
||||
prv_init_and_goto_awaiting_reset_complete_remote_initiated();
|
||||
|
||||
const PostMessageResetCompletePayload unsupported = {
|
||||
.min_supported_version = POSTMESSAGE_PROTOCOL_MAX_VERSION + 1,
|
||||
.max_supported_version = POSTMESSAGE_PROTOCOL_MAX_VERSION + 1,
|
||||
.max_tx_chunk_size = POSTMESSAGE_PROTOCOL_MAX_TX_CHUNK_SIZE,
|
||||
.max_rx_chunk_size = POSTMESSAGE_PROTOCOL_MAX_RX_CHUNK_SIZE,
|
||||
};
|
||||
RCV_APP_MESSAGE(TupletBytes(PostMessageKeyResetComplete,
|
||||
(const uint8_t *)&unsupported, sizeof(unsupported)));
|
||||
|
||||
// Expect No UnsupportedError!
|
||||
|
||||
// Immediately go back to AwaitingResetRequest:
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateAwaitingResetRequest);
|
||||
EXPECT_OUTBOX_NO_MESSAGE_PENDING();
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__awaiting_reset_complete_rem_init__malformed_reset_complete(void) {
|
||||
prv_init_and_goto_awaiting_reset_complete_remote_initiated();
|
||||
|
||||
// Receive malformed ResetComplete:
|
||||
uint8_t malformed_payload[sizeof(PostMessageResetCompletePayload) - 1] = {};
|
||||
RCV_APP_MESSAGE(TupletBytes(PostMessageKeyResetComplete,
|
||||
malformed_payload, sizeof(malformed_payload)));
|
||||
|
||||
// Expect Error:
|
||||
const PostMessageUnsupportedErrorPayload expected_error = {
|
||||
.error_code = PostMessageErrorMalformedResetComplete,
|
||||
};
|
||||
EXPECT_OUTBOX_MESSAGE_PENDING(TupletBytes(PostMessageKeyUnsupportedError,
|
||||
(const uint8_t *) &expected_error,
|
||||
sizeof(expected_error)));
|
||||
|
||||
// Immediately go back to AwaitingResetRequest:
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateAwaitingResetRequest);
|
||||
prv_rcv_app_message_ack(APP_MSG_OK);
|
||||
EXPECT_OUTBOX_NO_MESSAGE_PENDING();
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__awaiting_reset_complete_rem_init__receive_request(void) {
|
||||
prv_init_and_goto_awaiting_reset_complete_remote_initiated();
|
||||
|
||||
RCV_RESET_REQUEST();
|
||||
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(),
|
||||
PostMessageStateAwaitingResetCompleteRemoteInitiated);
|
||||
|
||||
EXPECT_OUTBOX_RESET_COMPLETE_PENDING();
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__awaiting_reset_complete_rem_init__receive_chunk(void) {
|
||||
prv_init_and_goto_awaiting_reset_complete_remote_initiated();
|
||||
|
||||
RCV_DUMMY_CHUNK();
|
||||
|
||||
EXPECT_OUTBOX_RESET_REQUEST();
|
||||
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(),
|
||||
PostMessageStateAwaitingResetCompleteLocalInitiated);
|
||||
|
||||
// Receive yet another chunk in "Awaiting Reset Complete Local Initiated":
|
||||
RCV_DUMMY_CHUNK();
|
||||
// https://pebbletechnology.atlassian.net/browse/PBL-42466
|
||||
// TODO: assert that chunk is NACKd
|
||||
|
||||
// Receive ACK for the ResetRequest:
|
||||
prv_rcv_app_message_ack(APP_MSG_OK);
|
||||
|
||||
// Chunk is ignored, no new reset request is sent out.
|
||||
EXPECT_OUTBOX_NO_MESSAGE_PENDING();
|
||||
|
||||
// TODO: timeout + retry ResetRequest if no ResetComplete follows within N secs.
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__awaiting_reset_complete_loc_init__(void) {
|
||||
prv_init_and_goto_awaiting_reset_complete_local_initiated();
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__awaiting_reset_complete_loc_init__rcv_reset_request(void) {
|
||||
prv_init_and_goto_awaiting_reset_complete_local_initiated();
|
||||
|
||||
RCV_RESET_REQUEST();
|
||||
|
||||
EXPECT_OUTBOX_RESET_COMPLETE_PENDING();
|
||||
prv_rcv_app_message_ack(APP_MSG_OK);
|
||||
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(),
|
||||
PostMessageStateAwaitingResetCompleteRemoteInitiated);
|
||||
|
||||
RCV_RESET_COMPLETE();
|
||||
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateSessionOpen);
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__awaiting_reset_complete_loc_init__rcv_chunk(void) {
|
||||
prv_init_and_goto_awaiting_reset_complete_local_initiated();
|
||||
|
||||
RCV_DUMMY_CHUNK();
|
||||
|
||||
// https://pebbletechnology.atlassian.net/browse/PBL-42466
|
||||
// TODO: assert that chunk is NACK'd
|
||||
|
||||
// Chunk is ignored, state isn't changed:
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(),
|
||||
PostMessageStateAwaitingResetCompleteLocalInitiated);
|
||||
EXPECT_OUTBOX_NO_MESSAGE_PENDING();
|
||||
}
|
||||
|
||||
static void prv_init_and_goto_session_open(void) {
|
||||
prv_init_and_goto_awaiting_reset_complete_remote_initiated();
|
||||
RCV_RESET_COMPLETE();
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateSessionOpen);
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__session_open__rcv_reset_request(void) {
|
||||
prv_init_and_goto_session_open();
|
||||
|
||||
EXECUTE_SCRIPT("var isCalled = false;"
|
||||
"_rocky.on('postmessagedisconnected', function() { isCalled = true; });");
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_B("isCalled", false);
|
||||
|
||||
RCV_RESET_REQUEST();
|
||||
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(),
|
||||
PostMessageStateAwaitingResetCompleteRemoteInitiated);
|
||||
EXPECT_OUTBOX_RESET_COMPLETE_PENDING();
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_B("isCalled", true);
|
||||
|
||||
// TODO: assert:
|
||||
// - flushed recv chunk reassembly buffer
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__session_open__rcv_reset_complete(void) {
|
||||
prv_init_and_goto_session_open();
|
||||
|
||||
EXECUTE_SCRIPT("var isCalled = false;"
|
||||
"_rocky.on('postmessagedisconnected', function() { isCalled = true; });");
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_B("isCalled", false);
|
||||
|
||||
RCV_RESET_COMPLETE();
|
||||
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(),
|
||||
PostMessageStateAwaitingResetCompleteLocalInitiated);
|
||||
EXPECT_OUTBOX_RESET_REQUEST();
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_B("isCalled", true);
|
||||
|
||||
// TODO: assert:
|
||||
// - flushed recv chunk reassembly buffer
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// postmessageconnected / postmessagedisconnected
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void prv_postmessageconnected_postmessagedisconnected_init(bool start_connected) {
|
||||
prv_init_api(start_connected);
|
||||
|
||||
EXECUTE_SCRIPT("var c = 0; var d = 0;\n"
|
||||
"_rocky.on('postmessageconnected', function() { c++; });\n"
|
||||
"_rocky.on('postmessagedisconnected', function() { d++; });\n");
|
||||
|
||||
// Make sure this race is handled (see comment in prv_handle_connection()):
|
||||
prv_simulate_transport_connection_event(start_connected /* is_connected */);
|
||||
}
|
||||
|
||||
static void prv_postmessageconnected_postmessagedisconnected_negotiate_to_open_session(void) {
|
||||
// Negotiate:
|
||||
RCV_RESET_REQUEST();
|
||||
|
||||
EXPECT_OUTBOX_RESET_COMPLETE_PENDING();
|
||||
prv_rcv_app_message_ack(APP_MSG_OK);
|
||||
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(),
|
||||
PostMessageStateAwaitingResetCompleteRemoteInitiated);
|
||||
|
||||
RCV_RESET_COMPLETE();
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateSessionOpen);
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__postmessageconnected_and_postmessagedisconnected_remote_rr(void) {
|
||||
prv_postmessageconnected_postmessagedisconnected_init(false /* start_connected */);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("d", 1);
|
||||
prv_simulate_transport_connection_event(true /* is_connected */);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("c", 0);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("d", 1);
|
||||
prv_postmessageconnected_postmessagedisconnected_negotiate_to_open_session();
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("c", 1);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("d", 1);
|
||||
|
||||
// Get a ResetRequest:
|
||||
RCV_RESET_REQUEST();
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("d", 2);
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__postmessageconnected_and_postmessagedisconnected_local_rr(void) {
|
||||
prv_postmessageconnected_postmessagedisconnected_init(false /* start_connected */);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("d", 1);
|
||||
prv_simulate_transport_connection_event(true /* is_connected */);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("c", 0);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("d", 1);
|
||||
prv_postmessageconnected_postmessagedisconnected_negotiate_to_open_session();
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("c", 1);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("d", 1);
|
||||
|
||||
// Get a ResetComplete (unexpected message), should trigger initiating (local) ResetRequest:
|
||||
RCV_RESET_COMPLETE();
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("d", 2);
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__postmessageconnected_and_postmessagedisconnected_start_conn(void) {
|
||||
prv_postmessageconnected_postmessagedisconnected_init(true /* start_connected */);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("c", 0);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("d", 1);
|
||||
|
||||
prv_postmessageconnected_postmessagedisconnected_negotiate_to_open_session();
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("c", 1);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("d", 1);
|
||||
}
|
||||
|
||||
// TODO: test various min/max version combos
|
||||
// TODO: test RX/TX buffer size combos
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Generic Tests
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void test_rocky_api_app_message__json_stringify(void) {
|
||||
JS_VAR obj = jerry_create_object();
|
||||
JS_VAR json_str = prv_json_stringify(obj);
|
||||
char *json_c_str = rocky_string_alloc_and_copy(json_str);
|
||||
cl_assert_equal_s(json_c_str, "{}");
|
||||
task_free(json_c_str);
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__json_parse(void) {
|
||||
JS_VAR number = prv_json_parse("1");
|
||||
cl_assert(jerry_value_is_number(number));
|
||||
cl_assert_equal_d(jerry_get_number_value(number), 1.0);
|
||||
|
||||
JS_VAR object = prv_json_parse("{ \"x\" : 42 }");
|
||||
cl_assert(jerry_value_is_object(object));
|
||||
JS_VAR x = jerry_get_object_field(object, "x");
|
||||
cl_assert(jerry_value_is_number(x));
|
||||
cl_assert_equal_d(jerry_get_number_value(x), 42.0);
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__json_parse_stress(void) {
|
||||
const int num_attempts = 0x3ff + 10; // Want this to be higher than the max refcount,
|
||||
// which will also be high enough for a memory stress test
|
||||
for (int i = 0; i < num_attempts; ++i) {
|
||||
JS_UNUSED_VAL = prv_json_parse(
|
||||
"var msg = { "
|
||||
"\"key\" : "
|
||||
"\"Bacon ipsum dolor amet kevin filet mignon id ut, aute sausage tri-tip "
|
||||
"frankfurter pork loin. Boudin ullamco landjaeger, kevin tongue minim tri-tip "
|
||||
"ground round dolore. Ham hock tongue swine, cillum jowl pancetta fugiat "
|
||||
"deserunt sirloin fatback tenderloin culpa andouille. Incididunt qui bacon "
|
||||
"nostrud ham hock adipisicing et ham. Ullamco esse eu capicola, ea culpa irure "
|
||||
"meatball proident laboris ut reprehenderit ex incididunt.\" };\n");
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// .postMessage() Tests
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define SIMPLE_TEST_OBJECT "{ \"x\" : 1 }"
|
||||
|
||||
static void prv_assert_simple_test_object_pending(void) {
|
||||
const char * const expected_json = "{\"x\":1}";
|
||||
const size_t expected_json_size = strlen(expected_json) + 1;
|
||||
const size_t expected_size = sizeof(PostMessageChunkPayload) + strlen(expected_json) + 1;
|
||||
uint8_t *buffer = task_malloc(expected_size);
|
||||
|
||||
PostMessageChunkPayload *chunk = (PostMessageChunkPayload *)buffer;
|
||||
*chunk = (PostMessageChunkPayload) {
|
||||
.total_size_bytes = expected_json_size,
|
||||
.is_first = true,
|
||||
};
|
||||
memcpy(chunk->chunk_data, expected_json, expected_json_size);
|
||||
|
||||
EXPECT_OUTBOX_MESSAGE_PENDING(TupletBytes(PostMessageKeyChunk,
|
||||
(const uint8_t *) chunk, expected_size));
|
||||
|
||||
// Compare with hard-coded byte array, to catch accidental changes to the ABI:
|
||||
const uint8_t raw_bytes_v1[] = {
|
||||
0x08, 0x00, 0x00, 0x80, 0x7b, 0x22, 0x78, 0x22, 0x3a, 0x31, 0x7d, 0x00,
|
||||
};
|
||||
cl_assert_equal_i(sizeof(raw_bytes_v1), expected_size);
|
||||
cl_assert_equal_m(raw_bytes_v1, buffer, expected_size);
|
||||
|
||||
task_free(buffer);
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__postmessage_just_before_connected(void) {
|
||||
prv_init_api(false /* start_connected */);
|
||||
|
||||
EXECUTE_SCRIPT("var x = " SIMPLE_TEST_OBJECT ";"
|
||||
"var hasError = false;"
|
||||
"_rocky.on('postmessageerror', function() { hasError = true; });"
|
||||
"_rocky.postMessage(x);");
|
||||
|
||||
// First send attempt fails because not in SessionOpen
|
||||
ASSERT_JS_GLOBAL_EQUALS_B("hasError", false);
|
||||
|
||||
prv_simulate_transport_connection_event(true /* is_connected */);
|
||||
prv_postmessageconnected_postmessagedisconnected_negotiate_to_open_session();
|
||||
|
||||
prv_assert_simple_test_object_pending();
|
||||
|
||||
prv_rcv_app_message_ack(APP_MSG_OK);
|
||||
|
||||
EXPECT_OUTBOX_NO_MESSAGE_PENDING();
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_B("hasError", false);
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__post_message_single_chunk(void) {
|
||||
prv_init_and_goto_session_open();
|
||||
|
||||
EXECUTE_SCRIPT("var x = " SIMPLE_TEST_OBJECT "; _rocky.postMessage(x);");
|
||||
prv_assert_simple_test_object_pending();
|
||||
|
||||
prv_rcv_app_message_ack(APP_MSG_OK);
|
||||
|
||||
EXPECT_OUTBOX_NO_MESSAGE_PENDING();
|
||||
}
|
||||
|
||||
static void prv_init_and_goto_session_open_with_tiny_buffers(void) {
|
||||
prv_init_and_goto_awaiting_reset_complete_remote_initiated();
|
||||
RCV_APP_MESSAGE(TupletBytes(PostMessageKeyResetComplete, \
|
||||
(const uint8_t *)&TINY_RESET_COMPLETE, sizeof(TINY_RESET_COMPLETE)));
|
||||
cl_assert_equal_i(rocky_api_app_message_get_state(), PostMessageStateSessionOpen);
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__post_message_multi_chunk(void) {
|
||||
prv_init_and_goto_session_open_with_tiny_buffers();
|
||||
|
||||
EXECUTE_SCRIPT("var x = { \"x\" : 123 }; _rocky.postMessage(x);");
|
||||
|
||||
const char * const expected_json = "{\"x\":123}";
|
||||
const size_t expected_json_size = strlen(expected_json) + 1;
|
||||
size_t json_bytes_remaining = expected_json_size;
|
||||
|
||||
uint8_t *buffer = task_malloc(sizeof(PostMessageChunkPayload) + TINY_CHUNK_SIZE);
|
||||
|
||||
// Chunk 1:
|
||||
{
|
||||
const size_t json_bytes_size = MIN(TINY_CHUNK_SIZE, json_bytes_remaining);
|
||||
const size_t expected_size = sizeof(PostMessageChunkPayload) + json_bytes_size;
|
||||
|
||||
PostMessageChunkPayload *chunk = (PostMessageChunkPayload *)buffer;
|
||||
*chunk = (PostMessageChunkPayload) {
|
||||
.total_size_bytes = expected_json_size,
|
||||
.is_first = true,
|
||||
};
|
||||
memcpy(chunk->chunk_data, expected_json, TINY_CHUNK_SIZE);
|
||||
|
||||
EXPECT_OUTBOX_MESSAGE_PENDING(TupletBytes(PostMessageKeyChunk,
|
||||
(const uint8_t *) chunk, expected_size));
|
||||
|
||||
// Compare with hard-coded byte array, to catch accidental changes to the ABI:
|
||||
const uint8_t raw_bytes_v1[] = {
|
||||
0x0a, 0x00, 0x00, 0x80, '{', '"', 'x', '"',
|
||||
};
|
||||
cl_assert_equal_i(sizeof(raw_bytes_v1), expected_size);
|
||||
cl_assert_equal_m(raw_bytes_v1, buffer, expected_size);
|
||||
|
||||
prv_rcv_app_message_ack(APP_MSG_OK);
|
||||
json_bytes_remaining -= json_bytes_size;
|
||||
}
|
||||
|
||||
// Chunk 2:
|
||||
{
|
||||
const size_t json_bytes_size = MIN(TINY_CHUNK_SIZE, json_bytes_remaining);
|
||||
const size_t expected_size = sizeof(PostMessageChunkPayload) + json_bytes_size;
|
||||
const int payload_offset = expected_json_size - json_bytes_remaining;
|
||||
|
||||
PostMessageChunkPayload *chunk = (PostMessageChunkPayload *)buffer;
|
||||
*chunk = (PostMessageChunkPayload) {
|
||||
.offset_bytes = payload_offset,
|
||||
.continuation_is_first = false,
|
||||
};
|
||||
memcpy(chunk->chunk_data, expected_json + payload_offset, TINY_CHUNK_SIZE);
|
||||
|
||||
EXPECT_OUTBOX_MESSAGE_PENDING(TupletBytes(PostMessageKeyChunk,
|
||||
(const uint8_t *) chunk, expected_size));
|
||||
|
||||
// Compare with hard-coded byte array, to catch accidental changes to the ABI:
|
||||
const uint8_t raw_bytes_v1[] = {
|
||||
0x04, 0x00, 0x00, 0x00, ':', '1', '2', '3',
|
||||
};
|
||||
cl_assert_equal_i(sizeof(raw_bytes_v1), expected_size);
|
||||
cl_assert_equal_m(raw_bytes_v1, buffer, expected_size);
|
||||
|
||||
prv_rcv_app_message_ack(APP_MSG_OK);
|
||||
json_bytes_remaining -= json_bytes_size;
|
||||
}
|
||||
|
||||
// Chunk 3:
|
||||
{
|
||||
const size_t json_bytes_size = MIN(TINY_CHUNK_SIZE, json_bytes_remaining);
|
||||
const size_t expected_size = sizeof(PostMessageChunkPayload) + json_bytes_size;
|
||||
const int payload_offset = expected_json_size - json_bytes_remaining;
|
||||
|
||||
PostMessageChunkPayload *chunk = (PostMessageChunkPayload *)buffer;
|
||||
*chunk = (PostMessageChunkPayload) {
|
||||
.offset_bytes = payload_offset,
|
||||
.continuation_is_first = false,
|
||||
};
|
||||
memcpy(chunk->chunk_data, expected_json + payload_offset, TINY_CHUNK_SIZE);
|
||||
|
||||
EXPECT_OUTBOX_MESSAGE_PENDING(TupletBytes(PostMessageKeyChunk,
|
||||
(const uint8_t *) chunk, expected_size));
|
||||
|
||||
// Compare with hard-coded byte array, to catch accidental changes to the ABI:
|
||||
const uint8_t raw_bytes_v1[] = {
|
||||
0x08, 0x00, 0x00, 0x00, '}', '\0',
|
||||
};
|
||||
cl_assert_equal_i(sizeof(raw_bytes_v1), expected_size);
|
||||
cl_assert_equal_m(raw_bytes_v1, buffer, expected_size);
|
||||
|
||||
prv_rcv_app_message_ack(APP_MSG_OK);
|
||||
json_bytes_remaining -= json_bytes_size;
|
||||
}
|
||||
|
||||
EXPECT_OUTBOX_NO_MESSAGE_PENDING();
|
||||
|
||||
task_free(buffer);
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__postmessage_not_jsonable(void) {
|
||||
prv_init_and_goto_session_open();
|
||||
|
||||
const char *not_jsonable_error =
|
||||
"TypeError: Argument at index 0 is not a JSON.stringify()-able object";
|
||||
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR("_rocky.postMessage(undefined);", not_jsonable_error);
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR("_rocky.postMessage(function() {});", not_jsonable_error);
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR("_rocky.postMessage({toJSON: function() {throw 'toJSONError';}});",
|
||||
"toJSONError");
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__postmessage_no_args(void) {
|
||||
prv_init_api(false /* start_connected */);
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR("_rocky.postMessage();", "TypeError: Not enough arguments");
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__postmessage_oom(void) {
|
||||
prv_init_api(false /* start_connected */);
|
||||
|
||||
fake_malloc_set_largest_free_block(0);
|
||||
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR("_rocky.postMessage('x');",
|
||||
"RangeError: Out of memory: can't postMessage() -- object too large");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Receive Tests
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void test_rocky_api_app_message__receive_message_multi_chunk(void) {
|
||||
prv_init_and_goto_session_open_with_tiny_buffers();
|
||||
|
||||
EXECUTE_SCRIPT("var event = null;\n"
|
||||
"var json_str = null;\n"
|
||||
"_rocky.on('message', function(e) {\n"
|
||||
" json_str = JSON.stringify(e.data);\n" // stringify again to make assert simple
|
||||
" event = e;\n"
|
||||
"});");
|
||||
JS_VAR event_null = prv_js_global_get_value("event");
|
||||
cl_assert_equal_b(true, jerry_value_is_null(event_null));
|
||||
|
||||
// Get 3x the same message in a row:
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
|
||||
// Chunks for: {"x":123}
|
||||
const struct {
|
||||
uint8_t byte_array[8];
|
||||
size_t length;
|
||||
} chunk_msg_defs[] = {
|
||||
{
|
||||
.byte_array = {0x0a, 0x00, 0x00, 0x80, '{', '"', 'x', '"'},
|
||||
.length = 8,
|
||||
},
|
||||
{
|
||||
.byte_array = {0x04, 0x00, 0x00, 0x00, ':', '1', '2', '3'},
|
||||
.length = 8,
|
||||
},
|
||||
{
|
||||
.byte_array = {0x08, 0x00, 0x00, 0x00, '}', '\0'},
|
||||
.length = 6,
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LENGTH(chunk_msg_defs); ++i) {
|
||||
RCV_APP_MESSAGE(TupletBytes(PostMessageKeyChunk,
|
||||
(const uint8_t *) chunk_msg_defs[i].byte_array,
|
||||
chunk_msg_defs[i].length));
|
||||
}
|
||||
|
||||
JS_VAR event_valid = prv_js_global_get_value("event");
|
||||
cl_assert_equal_b(true, jerry_value_is_object(event_valid));
|
||||
|
||||
// Make sure that there is a "data" property
|
||||
JS_VAR data_prop = jerry_get_object_field(event_valid, "data");
|
||||
cl_assert_equal_b(true, jerry_value_is_object(data_prop));
|
||||
|
||||
// Make sure the re-serialized object matches:
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("json_str", "{\"x\":123}");
|
||||
|
||||
EXECUTE_SCRIPT("json_str = null;\n"
|
||||
"event = null");
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// "postmessageerror" event
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void test_rocky_api_app_message__postmessageerror(void) {
|
||||
prv_init_and_goto_session_open();
|
||||
|
||||
EXECUTE_SCRIPT("var didError = false;"
|
||||
"var x = { \"x\" : 1 };"
|
||||
"var dataJSON = undefined;"
|
||||
"_rocky.on('postmessageerror', "
|
||||
" function(e) { didError = true; dataJSON = JSON.stringify(e.data); });"
|
||||
"_rocky.postMessage(x);"
|
||||
"x.x = 2;");
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_B("didError", false);
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
cl_assert_equal_b(s_is_outbox_message_pending, true);
|
||||
|
||||
// NACK
|
||||
prv_rcv_app_message_ack(APP_MSG_BUSY);
|
||||
|
||||
AppTimer *t = rocky_api_app_message_get_app_msg_retry_timer();
|
||||
cl_assert(t != EVENTED_TIMER_INVALID_ID);
|
||||
cl_assert_equal_b(fake_app_timer_is_scheduled(t), true);
|
||||
|
||||
// Enqueuing more objects shouldn't affect the pace at which things are retried:
|
||||
EXECUTE_SCRIPT("_rocky.postMessage('')");
|
||||
|
||||
EXPECT_OUTBOX_NO_MESSAGE_PENDING();
|
||||
|
||||
cl_assert_equal_b(app_timer_trigger(t), true);
|
||||
}
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_B("didError", true);
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("dataJSON", "{\"x\":1}");
|
||||
}
|
||||
|
||||
void test_rocky_api_app_message__postmessageerror_while_disconnected(void) {
|
||||
prv_init_api(false /* start_connected */);
|
||||
|
||||
EXECUTE_SCRIPT("var didError = false;"
|
||||
"var x = " SIMPLE_TEST_OBJECT ";"
|
||||
"_rocky.on('postmessageerror', "
|
||||
" function(e) { didError = true; dataJSON = JSON.stringify(e.data); });"
|
||||
/* 3x postMessage(): */
|
||||
"_rocky.postMessage(x);"
|
||||
"_rocky.postMessage(x);"
|
||||
"_rocky.postMessage(x);");
|
||||
|
||||
// Let the first 2 timeout:
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
ASSERT_JS_GLOBAL_EQUALS_B("didError", false);
|
||||
|
||||
AppTimer *t = rocky_api_app_message_get_session_closed_object_queue_timer();
|
||||
cl_assert(t != EVENTED_TIMER_INVALID_ID);
|
||||
cl_assert_equal_b(fake_app_timer_is_scheduled(t), true);
|
||||
|
||||
EXPECT_OUTBOX_NO_MESSAGE_PENDING();
|
||||
|
||||
cl_assert_equal_b(app_timer_trigger(t), true);
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_B("didError", true);
|
||||
|
||||
EXECUTE_SCRIPT("didError = false;");
|
||||
}
|
||||
|
||||
// Timer for the 3rd should be set:
|
||||
AppTimer *t = rocky_api_app_message_get_session_closed_object_queue_timer();
|
||||
cl_assert(t != EVENTED_TIMER_INVALID_ID);
|
||||
cl_assert_equal_b(fake_app_timer_is_scheduled(t), true);
|
||||
|
||||
// Connect:
|
||||
prv_simulate_transport_connection_event(true /* is_connected */);
|
||||
prv_postmessageconnected_postmessagedisconnected_negotiate_to_open_session();
|
||||
|
||||
// Timer for the 3rd should be cancelled now:
|
||||
cl_assert(EVENTED_TIMER_INVALID_ID == rocky_api_app_message_get_session_closed_object_queue_timer());
|
||||
|
||||
prv_assert_simple_test_object_pending();
|
||||
}
|
122
tests/fw/javascript/test_rocky_api_console.c
Normal file
122
tests/fw/javascript/test_rocky_api_console.c
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
#include "test_jerry_port_common.h"
|
||||
#include "test_rocky_common.h"
|
||||
|
||||
#include "applib/rockyjs/api/rocky_api_global.h"
|
||||
#include "applib/rockyjs/api/rocky_api_console.h"
|
||||
#include "applib/rockyjs/pbl_jerry_port.h"
|
||||
|
||||
// Standard
|
||||
#include "string.h"
|
||||
|
||||
// Fakes
|
||||
#include "fake_app_timer.h"
|
||||
#include "fake_time.h"
|
||||
#include "fake_logging.h"
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_manager.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_sleep.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_sys_exit.h"
|
||||
|
||||
size_t heap_bytes_free(void) {
|
||||
return 123456;
|
||||
}
|
||||
|
||||
static const RockyGlobalAPI *s_api[] = {
|
||||
&CONSOLE_APIS,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void test_rocky_api_console__initialize(void) {
|
||||
rocky_runtime_context_init();
|
||||
jerry_init(JERRY_INIT_EMPTY);
|
||||
s_log_internal__expected = NULL;
|
||||
}
|
||||
|
||||
void test_rocky_api_console__cleanup(void) {
|
||||
jerry_cleanup();
|
||||
rocky_runtime_context_deinit();
|
||||
}
|
||||
|
||||
void test_rocky_api_console__functions_exist(void) {
|
||||
rocky_global_init(s_api);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"var c = typeof console;\n"
|
||||
"var cl = typeof console.log;\n"
|
||||
"var cw = typeof console.warn;\n"
|
||||
"var ce = typeof console.error;\n"
|
||||
);
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("c", "object");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("cl", "function");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("cw", "function");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("ce", "function");
|
||||
}
|
||||
|
||||
void test_rocky_api_console__logs_single_values(void) {
|
||||
rocky_global_init(s_api);
|
||||
|
||||
s_log_internal__expected = (const char *[]){
|
||||
"some string",
|
||||
"1234",
|
||||
"true",
|
||||
"undefined",
|
||||
"[object Object]",
|
||||
NULL
|
||||
};
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"console.log('some string');\n"
|
||||
"console.log(1230 + 4);\n"
|
||||
"console.log(1 == 1);\n"
|
||||
"console.log(undefined);\n"
|
||||
"console.log({a:123, b:[1,2]});\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_s(NULL, *s_log_internal__expected);
|
||||
}
|
||||
|
||||
void test_rocky_api_console__warn_error_multiple(void) {
|
||||
rocky_global_init(s_api);
|
||||
|
||||
s_log_internal__expected = (const char *[]){
|
||||
"foo",
|
||||
"1",
|
||||
"2",
|
||||
"true",
|
||||
"false",
|
||||
NULL
|
||||
};
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"console.warn('foo', 1, 2);\n"
|
||||
"console.error(true, false);\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_s(NULL, *s_log_internal__expected);
|
||||
}
|
326
tests/fw/javascript/test_rocky_api_datetime.c
Normal file
326
tests/fw/javascript/test_rocky_api_datetime.c
Normal file
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
#include "test_jerry_port_common.h"
|
||||
#include "test_rocky_common.h"
|
||||
|
||||
#include "applib/rockyjs/api/rocky_api_global.h"
|
||||
#include "applib/rockyjs/api/rocky_api_datetime.h"
|
||||
#include "applib/rockyjs/pbl_jerry_port.h"
|
||||
|
||||
// Standard
|
||||
#include <string.h>
|
||||
|
||||
// Fakes
|
||||
#include "fake_app_timer.h"
|
||||
#include "fake_time.h"
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_manager.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_sleep.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_sys_exit.h"
|
||||
|
||||
size_t heap_bytes_free(void) {
|
||||
return 123456;
|
||||
}
|
||||
|
||||
static bool s_clock_is_24h_style;
|
||||
bool clock_is_24h_style() {
|
||||
return s_clock_is_24h_style;
|
||||
}
|
||||
|
||||
|
||||
void test_rocky_api_datetime__initialize(void) {
|
||||
// Mon Jul 25 2005 20:04:05 GMT-03:00
|
||||
s_time = 1122332645;
|
||||
s_gmt_off = -3 * 60 * 60;
|
||||
|
||||
s_clock_is_24h_style = false;
|
||||
fake_app_timer_init();
|
||||
rocky_runtime_context_init();
|
||||
jerry_init(JERRY_INIT_EMPTY);
|
||||
}
|
||||
|
||||
void test_rocky_api_datetime__cleanup(void) {
|
||||
jerry_cleanup();
|
||||
rocky_runtime_context_deinit();
|
||||
}
|
||||
|
||||
static const RockyGlobalAPI *s_api[] = {
|
||||
&DATETIME_APIS,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void test_rocky_api_datetime__jerry_script_default(void) {
|
||||
static const RockyGlobalAPI *apis[] = {NULL};
|
||||
rocky_global_init(apis);
|
||||
EXECUTE_SCRIPT(
|
||||
"var d = new Date();\n"
|
||||
"var s1 = d.toString();\n"
|
||||
"var f = typeof(d.toLocaleTimeString);\n"
|
||||
"var s2 = d.toLocaleTimeString();\n"
|
||||
"var s3 = d.toLocaleDateString();\n"
|
||||
"var s4 = d.toLocaleString();\n"
|
||||
);
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s1", "Mon Jul 25 2005 20:04:05 GMT-03:00");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("f", "function");
|
||||
// yes, JerryScript provides some default behavior but it's not what we want
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s2", "23:04:05.000");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s3", "2005-07-25");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s4", "Mon Jul 25 2005 20:04:05 GMT-03:00");
|
||||
}
|
||||
|
||||
void test_rocky_api_datetime__locale_time_string_12h(void) {
|
||||
s_clock_is_24h_style = false;
|
||||
rocky_global_init(s_api);
|
||||
EXECUTE_SCRIPT(
|
||||
"var d = new Date();\n"
|
||||
"var s = d.toLocaleTimeString();\n"
|
||||
);
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "8:04:05 PM");
|
||||
|
||||
s_time += 4 * SECONDS_PER_HOUR;
|
||||
EXECUTE_SCRIPT(
|
||||
"var d = new Date();\n"
|
||||
"var s = d.toLocaleTimeString();\n"
|
||||
);
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "12:04:05 AM");
|
||||
}
|
||||
|
||||
void test_rocky_api_datetime__locale_time_string_24h(void) {
|
||||
s_clock_is_24h_style = true;
|
||||
rocky_global_init(s_api);
|
||||
EXECUTE_SCRIPT(
|
||||
"var d = new Date();\n"
|
||||
"var s = d.toLocaleTimeString();\n"
|
||||
);
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "20:04:05");
|
||||
|
||||
s_time += 4 * SECONDS_PER_HOUR;
|
||||
EXECUTE_SCRIPT(
|
||||
"var d = new Date();\n"
|
||||
"var s = d.toLocaleTimeString();\n"
|
||||
);
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "00:04:05");
|
||||
}
|
||||
|
||||
void test_rocky_api_datetime__locale(void) {
|
||||
rocky_global_init(s_api);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"var d = new Date();\n"
|
||||
"d.toLocaleTimeString(undefined);\n"
|
||||
);
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR(
|
||||
"d.toLocaleTimeString('en-us');",
|
||||
"TypeError: Unsupported locale"
|
||||
);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"var d = new Date();\n"
|
||||
"d.toLocaleDateString(undefined);\n"
|
||||
);
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR(
|
||||
"d.toLocaleDateString('de');",
|
||||
"TypeError: Unsupported locale"
|
||||
);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"var d = new Date();\n"
|
||||
"d.toLocaleString(undefined);\n"
|
||||
);
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR(
|
||||
"d.toLocaleString('de');",
|
||||
"TypeError: Unsupported locale"
|
||||
);
|
||||
}
|
||||
|
||||
void test_rocky_api_datetime__locale_time_string_options(void) {
|
||||
rocky_global_init(s_api);
|
||||
|
||||
EXECUTE_SCRIPT("var d = new Date();");
|
||||
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {second: 'numeric'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "5");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {second: '2-digit'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "05");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {minute: 'numeric'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "4");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {minute: '2-digit'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "04");
|
||||
|
||||
s_clock_is_24h_style = false;
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {hour: 'numeric'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "8 PM");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, "
|
||||
"{hour: 'numeric', hour12: true});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "8 PM");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, "
|
||||
"{hour: 'numeric', hour12: false});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "20");
|
||||
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {hour: '2-digit'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "08 PM");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "8:04:05 PM");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, "
|
||||
"{hour: undefined, minute: undefined, second: undefined});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "8:04:05 PM");
|
||||
|
||||
|
||||
s_clock_is_24h_style = true;
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {hour: 'numeric'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "20");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, "
|
||||
"{hour: 'numeric', hour12: true});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "8 PM");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, "
|
||||
"{hour: 'numeric', hour12: false});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "20");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {hour: '2-digit'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "20");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "20:04:05");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, "
|
||||
"{hour: undefined, minute: undefined, second: undefined});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "20:04:05");
|
||||
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR(
|
||||
"d.toLocaleTimeString(undefined, {minute: 'numeric', hour: '2-digit'})",
|
||||
"TypeError: Unsupported options"
|
||||
);
|
||||
}
|
||||
|
||||
void test_rocky_api_datetime__locale_time_string_date_options(void) {
|
||||
rocky_global_init(s_api);
|
||||
|
||||
EXECUTE_SCRIPT("var d = new Date();");
|
||||
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {day: 'short'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "Mon, 8:04:05 PM");
|
||||
}
|
||||
|
||||
void test_rocky_api_datetime__locale_date_string(void) {
|
||||
rocky_global_init(s_api);
|
||||
EXECUTE_SCRIPT(
|
||||
"var d = new Date();\n"
|
||||
"var s = d.toLocaleDateString();\n"
|
||||
);
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "07/25/05");
|
||||
}
|
||||
|
||||
void test_rocky_api_datetime__locale_date_string_options(void) {
|
||||
rocky_global_init(s_api);
|
||||
|
||||
EXECUTE_SCRIPT("var d = new Date();");
|
||||
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {day: 'numeric'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "25");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {day: '2-digit'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "25");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {day: 'short'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "Mon");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {day: 'long'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "Monday");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {month: 'numeric'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "7");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {month: '2-digit'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "07");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {month: 'short'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "Jul");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {month: 'long'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "July");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {year: 'numeric'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "2005");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {year: '2-digit'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "05");
|
||||
}
|
||||
|
||||
void test_rocky_api_datetime__locale_date_string_time_options(void) {
|
||||
rocky_global_init(s_api);
|
||||
|
||||
EXECUTE_SCRIPT("var d = new Date();");
|
||||
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {hour: 'numeric'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "07/25/05, 8 PM");
|
||||
}
|
||||
|
||||
void test_rocky_api_datetime__locale_string_options(void) {
|
||||
rocky_global_init(s_api);
|
||||
EXECUTE_SCRIPT("var d = new Date();");
|
||||
|
||||
s_clock_is_24h_style = false;
|
||||
EXECUTE_SCRIPT("s = d.toLocaleString(undefined, {});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "07/25/05, 8:04:05 PM");
|
||||
s_clock_is_24h_style = true;
|
||||
EXECUTE_SCRIPT("s = d.toLocaleString(undefined, {});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "07/25/05, 20:04:05");
|
||||
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {second: 'numeric'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "5");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {second: '2-digit'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "05");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {minute: 'numeric'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "4");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {minute: '2-digit'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "04");
|
||||
|
||||
s_clock_is_24h_style = false;
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {hour: 'numeric'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "8 PM");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {hour: '2-digit'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "08 PM");
|
||||
s_clock_is_24h_style = true;
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {hour: 'numeric'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "20");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleTimeString(undefined, {hour: '2-digit'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "20");
|
||||
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {day: 'numeric'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "25");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {day: '2-digit'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "25");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {day: 'short'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "Mon");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {day: 'long'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "Monday");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {month: 'numeric'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "7");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {month: '2-digit'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "07");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {month: 'short'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "Jul");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {month: 'long'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "July");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {year: 'numeric'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "2005");
|
||||
EXECUTE_SCRIPT("s = d.toLocaleDateString(undefined, {year: '2-digit'});");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "05");
|
||||
}
|
315
tests/fw/javascript/test_rocky_api_global.c
Normal file
315
tests/fw/javascript/test_rocky_api_global.c
Normal file
|
@ -0,0 +1,315 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
#include "test_jerry_port_common.h"
|
||||
#include "test_rocky_common.h"
|
||||
|
||||
#include "applib/rockyjs/api/rocky_api_global.h"
|
||||
#include "applib/rockyjs/pbl_jerry_port.h"
|
||||
|
||||
// Standard
|
||||
#include "string.h"
|
||||
|
||||
// Fakes
|
||||
#include "fake_app_timer.h"
|
||||
#include "fake_time.h"
|
||||
#include "fake_logging.h"
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_manager.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_sleep.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_sys_exit.h"
|
||||
|
||||
size_t heap_bytes_free(void) {
|
||||
return 123456;
|
||||
}
|
||||
|
||||
static Window s_app_window_stack_get_top_window;
|
||||
Window *app_window_stack_get_top_window() {
|
||||
return &s_app_window_stack_get_top_window;
|
||||
}
|
||||
|
||||
static int s_prv_api_init__callcount;
|
||||
static void prv_api_init(void) {
|
||||
s_prv_api_init__callcount++;
|
||||
}
|
||||
|
||||
static int s_prv_api_add__callcount;
|
||||
static bool s_prv_api_add__result;
|
||||
static bool prv_api_add(const char *event_name, jerry_value_t handler) {
|
||||
s_prv_api_add__callcount++;
|
||||
return s_prv_api_add__result;
|
||||
}
|
||||
|
||||
static int s_prv_api_remove__callcount;
|
||||
static void prv_api_remove(const char *event_name, jerry_value_t handler) {
|
||||
s_prv_api_remove__callcount++;
|
||||
}
|
||||
|
||||
int s_prv_listener_a1__callcount;
|
||||
JERRY_FUNCTION(prv_listener_a1) {
|
||||
s_prv_listener_a1__callcount++;
|
||||
return jerry_create_undefined();
|
||||
}
|
||||
|
||||
int s_prv_listener_a2__callcount;
|
||||
JERRY_FUNCTION(prv_listener_a2) {
|
||||
s_prv_listener_a2__callcount++;
|
||||
return jerry_create_undefined();
|
||||
}
|
||||
|
||||
int s_prv_listener_b__callcount;
|
||||
JERRY_FUNCTION(prv_listener_b) {
|
||||
s_prv_listener_b__callcount++;
|
||||
return jerry_create_undefined();
|
||||
}
|
||||
|
||||
void test_rocky_api_global__initialize(void) {
|
||||
fake_app_timer_init();
|
||||
rocky_runtime_context_init();
|
||||
jerry_init(JERRY_INIT_EMPTY);
|
||||
s_app_window_stack_get_top_window = (Window){};
|
||||
|
||||
s_app_event_loop_callback = NULL;
|
||||
s_log_internal__expected = NULL;
|
||||
|
||||
s_prv_api_init__callcount = 0;
|
||||
s_prv_api_add__callcount = 0;
|
||||
s_prv_api_add__result = false;
|
||||
s_prv_api_remove__callcount = 0;
|
||||
|
||||
s_prv_listener_a1__callcount = 0;
|
||||
s_prv_listener_a2__callcount = 0;
|
||||
s_prv_listener_b__callcount = 0;
|
||||
}
|
||||
|
||||
void test_rocky_api_global__cleanup(void) {
|
||||
fake_app_timer_deinit();
|
||||
s_log_internal__expected = NULL;
|
||||
|
||||
jerry_cleanup();
|
||||
rocky_runtime_context_deinit();
|
||||
rocky_global_deinit();
|
||||
}
|
||||
|
||||
void test_rocky_api_global__global(void) {
|
||||
char test_object[] = "var t = typeof _rocky";
|
||||
|
||||
// global doesn't exist in plain Jerry context
|
||||
EXECUTE_SCRIPT(test_object);
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("t", "undefined");
|
||||
|
||||
// rocky_global_init() injects global...
|
||||
static const RockyGlobalAPI *apis[] = {
|
||||
NULL,
|
||||
};
|
||||
rocky_global_init(apis);
|
||||
|
||||
EXECUTE_SCRIPT(test_object);
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("t", "object");
|
||||
|
||||
// ...which also has a method .on()
|
||||
EXECUTE_SCRIPT("var t = typeof _rocky.on");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("t", "function");
|
||||
|
||||
/// ...which is an alias of .addEventListener()
|
||||
EXECUTE_SCRIPT("var a = (_rocky.on === _rocky.addEventListener);");
|
||||
ASSERT_JS_GLOBAL_EQUALS_B("a", true);
|
||||
}
|
||||
|
||||
void test_rocky_api_global__calls_init_and_notifies_about_apis(void) {
|
||||
static const RockyGlobalAPI api = {
|
||||
.init = prv_api_init,
|
||||
.add_handler = prv_api_add,
|
||||
};
|
||||
static const RockyGlobalAPI *apis[] = {
|
||||
&api,
|
||||
NULL,
|
||||
};
|
||||
rocky_global_init(apis);
|
||||
cl_assert_equal_i(1, s_prv_api_init__callcount);
|
||||
|
||||
s_prv_api_add__result = true;
|
||||
s_log_internal__expected = (const char *[]){NULL};
|
||||
EXECUTE_SCRIPT("_rocky.on('foo', function(){})");
|
||||
cl_assert_equal_i(1, s_prv_api_add__callcount);
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("foo"));
|
||||
|
||||
s_prv_api_add__result = false;
|
||||
s_log_internal__expected = (const char *[]){
|
||||
"Unknown event 'bar'",
|
||||
NULL };
|
||||
EXECUTE_SCRIPT("_rocky.on('bar', function(){})");
|
||||
cl_assert_equal_i(2, s_prv_api_add__callcount);
|
||||
cl_assert_equal_b(false, rocky_global_has_event_handlers("bar"));
|
||||
cl_assert(*s_log_internal__expected == NULL);
|
||||
}
|
||||
|
||||
void test_rocky_api_global__can_unsubsribe_event_handlers(void) {
|
||||
static const RockyGlobalAPI api = {
|
||||
.add_handler = prv_api_add,
|
||||
.remove_handler = prv_api_remove,
|
||||
};
|
||||
static const RockyGlobalAPI *apis[] = {
|
||||
&api,
|
||||
NULL,
|
||||
};
|
||||
rocky_global_init(apis);
|
||||
|
||||
s_prv_api_add__result = true;
|
||||
EXECUTE_SCRIPT(
|
||||
"var f1 = function(){};\n"
|
||||
"var f2 = function(){};\n"
|
||||
"_rocky.on('foo', f1)\n"
|
||||
);
|
||||
cl_assert_equal_i(1, s_prv_api_add__callcount);
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("foo"));
|
||||
|
||||
// variables f1, f2 continue to exist between EXECUTE_SCRIPT calls
|
||||
EXECUTE_SCRIPT("var t = typeof f2;");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("t", "function");
|
||||
|
||||
// rocky.off exists
|
||||
EXECUTE_SCRIPT(
|
||||
"t = typeof _rocky.off;\n"
|
||||
"var eq = _rocky.off === _rocky.removeEventListener;\n"
|
||||
);
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("t", "function");
|
||||
ASSERT_JS_GLOBAL_EQUALS_B("eq", true);
|
||||
|
||||
// from MDN docs:
|
||||
// Calling removeEventListener() with arguments that do not identify
|
||||
// any currently registered EventListener on the EventTarget has no effect.
|
||||
EXECUTE_SCRIPT(
|
||||
"_rocky.off('foo', f2);\n"
|
||||
"_rocky.off('unknownevent', f1);\n"
|
||||
);
|
||||
cl_assert_equal_i(0, s_prv_api_remove__callcount);
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("foo"));
|
||||
|
||||
EXECUTE_SCRIPT("_rocky.off('foo', f1);\n");
|
||||
cl_assert_equal_i(1, s_prv_api_remove__callcount);
|
||||
cl_assert_equal_b(false, rocky_global_has_event_handlers("foo"));
|
||||
}
|
||||
|
||||
void prv_add_event_listener_to_list(const char *event_name, jerry_value_t listener);
|
||||
int jerry_obj_refcount(jerry_value_t o);
|
||||
|
||||
void test_rocky_api_global__refcount(void) {
|
||||
jerry_value_t o = jerry_create_object();
|
||||
cl_assert_equal_i(1, jerry_obj_refcount(o));
|
||||
jerry_acquire_value(o);
|
||||
cl_assert_equal_i(2, jerry_obj_refcount(o));
|
||||
jerry_acquire_value(o);
|
||||
cl_assert_equal_i(3, jerry_obj_refcount(o));
|
||||
jerry_release_value(o);
|
||||
cl_assert_equal_i(2, jerry_obj_refcount(o));
|
||||
jerry_release_value(o);
|
||||
cl_assert_equal_i(1, jerry_obj_refcount(o));
|
||||
jerry_release_value(o);
|
||||
cl_assert_equal_i(0, jerry_obj_refcount(o));
|
||||
}
|
||||
|
||||
void test_rocky_api_global__calls_listeners(void) {
|
||||
static const RockyGlobalAPI *apis[] = {NULL};
|
||||
rocky_global_init(apis);
|
||||
|
||||
prv_add_event_listener_to_list("a", jerry_create_external_function(prv_listener_a1));
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("a"));
|
||||
cl_assert_equal_b(false, rocky_global_has_event_handlers("b"));
|
||||
|
||||
|
||||
prv_add_event_listener_to_list("b", jerry_create_external_function(prv_listener_b));
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("a"));
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("b"));
|
||||
|
||||
prv_add_event_listener_to_list("a", jerry_create_external_function(prv_listener_a2));
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("a"));
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("b"));
|
||||
|
||||
jerry_value_t a_event = rocky_global_create_event("a");
|
||||
rocky_global_call_event_handlers(a_event);
|
||||
cl_assert_equal_i(1, s_prv_listener_a1__callcount);
|
||||
cl_assert_equal_i(1, s_prv_listener_a2__callcount);
|
||||
cl_assert_equal_i(0, s_prv_listener_b__callcount);
|
||||
jerry_release_value(a_event);
|
||||
|
||||
jerry_value_t b_event = rocky_global_create_event("b");
|
||||
rocky_global_call_event_handlers(b_event);
|
||||
cl_assert_equal_i(1, s_prv_listener_a1__callcount);
|
||||
cl_assert_equal_i(1, s_prv_listener_a2__callcount);
|
||||
cl_assert_equal_i(1, s_prv_listener_b__callcount);
|
||||
jerry_release_value(b_event);
|
||||
}
|
||||
|
||||
void test_rocky_api_global__adds_listener_only_once(void) {
|
||||
static const RockyGlobalAPI *apis[] = {NULL};
|
||||
rocky_global_init(apis);
|
||||
|
||||
const jerry_value_t f = jerry_create_external_function(prv_listener_a1);
|
||||
prv_add_event_listener_to_list("a", f);
|
||||
prv_add_event_listener_to_list("a", f);
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("a"));
|
||||
|
||||
jerry_value_t a_event = rocky_global_create_event("a");
|
||||
rocky_global_call_event_handlers(a_event);
|
||||
// as second .on('a', f) "replaces" first, f will only be called once
|
||||
cl_assert_equal_i(1, s_prv_listener_a1__callcount);
|
||||
jerry_release_value(a_event);
|
||||
}
|
||||
|
||||
void test_rocky_api_global__event_constructor(void) {
|
||||
static const RockyGlobalAPI *apis[] = {NULL};
|
||||
rocky_global_init(apis);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"_rocky.Event.prototype.myCustomThing = 'xyz';\n"
|
||||
"var e = new _rocky.Event('myevent');\n"
|
||||
"var t = e.type;\n"
|
||||
"var c = e.myCustomThing;\n"
|
||||
);
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("t", "myevent");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("c", "xyz");
|
||||
}
|
||||
|
||||
void test_rocky_api_global__call_event_handlers_async(void) {
|
||||
static const RockyGlobalAPI api = {
|
||||
.init = prv_api_init,
|
||||
.add_handler = prv_api_add,
|
||||
};
|
||||
static const RockyGlobalAPI *apis[] = {
|
||||
&api,
|
||||
NULL,
|
||||
};
|
||||
rocky_global_init(apis);
|
||||
|
||||
s_prv_api_add__result = true;
|
||||
EXECUTE_SCRIPT("var is_called = false; _rocky.on('a', function(e) { is_called = true; });");
|
||||
jerry_value_t a_event = rocky_global_create_event("a");
|
||||
rocky_global_call_event_handlers_async(a_event);
|
||||
ASSERT_JS_GLOBAL_EQUALS_B("is_called", false);
|
||||
|
||||
s_process_manager_callback(s_process_manager_callback_data);
|
||||
ASSERT_JS_GLOBAL_EQUALS_B("is_called", true);
|
||||
}
|
768
tests/fw/javascript/test_rocky_api_graphics.c
Normal file
768
tests/fw/javascript/test_rocky_api_graphics.c
Normal file
|
@ -0,0 +1,768 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
#include "test_jerry_port_common.h"
|
||||
#include "test_rocky_common.h"
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/rockyjs/api/rocky_api.h"
|
||||
#include "applib/rockyjs/api/rocky_api_global.h"
|
||||
#include "applib/rockyjs/api/rocky_api_graphics.h"
|
||||
#include "applib/rockyjs/api/rocky_api_graphics_text.h"
|
||||
#include "applib/rockyjs/pbl_jerry_port.h"
|
||||
#include "util/trig.h"
|
||||
|
||||
// Standard
|
||||
#include "string.h"
|
||||
|
||||
// Fakes
|
||||
#include "fake_app_timer.h"
|
||||
#include "fake_time.h"
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_manager.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_sleep.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_sys_exit.h"
|
||||
|
||||
size_t heap_bytes_free(void) {
|
||||
return 123456;
|
||||
}
|
||||
|
||||
static Window s_app_window_stack_get_top_window;
|
||||
Window *app_window_stack_get_top_window() {
|
||||
return &s_app_window_stack_get_top_window;
|
||||
}
|
||||
|
||||
void rocky_api_graphics_path2d_add_canvas_methods(jerry_value_t obj) {}
|
||||
void rocky_api_graphics_path2d_cleanup(void) {}
|
||||
void rocky_api_graphics_path2d_reset_state(void) {}
|
||||
|
||||
GContext s_context;
|
||||
|
||||
// mocks
|
||||
static MockCallRecordings s_graphics_context_set_fill_color;
|
||||
void graphics_context_set_fill_color(GContext* ctx, GColor color) {
|
||||
record_mock_call(s_graphics_context_set_fill_color) {
|
||||
.ctx = ctx, .color = color,
|
||||
};
|
||||
ctx->draw_state.fill_color = color;
|
||||
}
|
||||
|
||||
static MockCallRecordings s_graphics_context_set_stroke_color;
|
||||
void graphics_context_set_stroke_color(GContext* ctx, GColor color) {
|
||||
record_mock_call(s_graphics_context_set_stroke_color) {
|
||||
.ctx = ctx, .color = color,
|
||||
};
|
||||
ctx->draw_state.stroke_color = color;
|
||||
}
|
||||
|
||||
static MockCallRecordings s_graphics_context_set_stroke_width;
|
||||
void graphics_context_set_stroke_width(GContext* ctx, uint8_t stroke_width) {
|
||||
record_mock_call(s_graphics_context_set_stroke_width) {
|
||||
.ctx = ctx, .width = stroke_width,
|
||||
};
|
||||
ctx->draw_state.stroke_width = stroke_width;
|
||||
}
|
||||
|
||||
static MockCallRecordings s_graphics_fill_rect;
|
||||
static GColor s_graphics_fill_rect__color;
|
||||
void graphics_fill_rect(GContext *ctx, const GRect *rect) {
|
||||
s_graphics_fill_rect__color = s_context.draw_state.fill_color;
|
||||
record_mock_call(s_graphics_fill_rect) {
|
||||
.ctx = ctx, .rect = *rect,
|
||||
};
|
||||
}
|
||||
|
||||
static MockCallRecordings s_graphics_draw_rect_precise;
|
||||
void graphics_draw_rect_precise(GContext *ctx, const GRectPrecise *rect) {
|
||||
record_mock_call(s_graphics_draw_rect_precise) {
|
||||
.ctx = ctx, .prect = *rect,
|
||||
};
|
||||
}
|
||||
|
||||
static MockCallRecordings s_graphics_fill_radial_precise_internal;
|
||||
void graphics_fill_radial_precise_internal(GContext *ctx, GPointPrecise center,
|
||||
Fixed_S16_3 radius_inner, Fixed_S16_3 radius_outer,
|
||||
int32_t angle_start, int32_t angle_end) {
|
||||
record_mock_call(s_graphics_fill_radial_precise_internal) {
|
||||
.ctx = ctx,
|
||||
.fill_radial_precise.center = center,
|
||||
.fill_radial_precise.radius_inner = radius_inner,
|
||||
.fill_radial_precise.radius_outer = radius_outer,
|
||||
.fill_radial_precise.angle_start = angle_start,
|
||||
.fill_radial_precise.angle_end = angle_end,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
void graphics_fill_round_rect_by_value(GContext *ctx, GRect rect, uint16_t corner_radius,
|
||||
GCornerMask corner_mask) {}
|
||||
static MockCallRecordings s_layer_mark_dirty;
|
||||
void layer_mark_dirty(Layer *layer) {
|
||||
record_mock_call(s_layer_mark_dirty){
|
||||
.layer = layer
|
||||
};
|
||||
}
|
||||
|
||||
static MockCallRecordings s_fonts_get_system_font;
|
||||
GFont s_fonts_get_system_font__result;
|
||||
GFont fonts_get_system_font(const char *font_key) {
|
||||
record_mock_call(s_fonts_get_system_font){
|
||||
.font_key = font_key
|
||||
};
|
||||
return s_fonts_get_system_font__result;
|
||||
}
|
||||
|
||||
static MockCallRecordings s_graphics_draw_text;
|
||||
void graphics_draw_text(GContext *ctx, const char *text, GFont const font, const GRect box,
|
||||
const GTextOverflowMode overflow_mode, const GTextAlignment alignment,
|
||||
GTextAttributes *text_attributes) {
|
||||
record_mock_call(s_graphics_draw_text){
|
||||
.draw_text.box = box,
|
||||
.draw_text.color = ctx->draw_state.text_color,
|
||||
};
|
||||
strncpy(s_graphics_draw_text.last_call.draw_text.text,
|
||||
text, sizeof(s_graphics_draw_text.last_call.draw_text.text));
|
||||
}
|
||||
|
||||
static MockCallRecordings s_graphics_text_attributes_destroy;
|
||||
void graphics_text_attributes_destroy(GTextAttributes *text_attributes) {
|
||||
record_mock_call(s_graphics_text_attributes_destroy){};
|
||||
}
|
||||
|
||||
static MockCallRecordings s_graphics_text_layout_get_max_used_size;
|
||||
static GSize s_graphics_text_layout_get_max_used_size__result;
|
||||
GSize graphics_text_layout_get_max_used_size(GContext *ctx, const char *text,
|
||||
GFont const font, const GRect box,
|
||||
const GTextOverflowMode overflow_mode,
|
||||
const GTextAlignment alignment,
|
||||
GTextLayoutCacheRef layout) {
|
||||
record_mock_call(s_graphics_text_layout_get_max_used_size){
|
||||
.max_used_size.font = font,
|
||||
.max_used_size.box = box,
|
||||
.max_used_size.overflow_mode = overflow_mode,
|
||||
.max_used_size.alignment = alignment,
|
||||
};
|
||||
strncpy(s_graphics_text_layout_get_max_used_size.last_call.max_used_size.text,
|
||||
text, sizeof(s_graphics_text_layout_get_max_used_size.last_call.max_used_size.text));
|
||||
|
||||
return s_graphics_text_layout_get_max_used_size__result;
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__initialize(void) {
|
||||
fake_app_timer_init();
|
||||
rocky_runtime_context_init();
|
||||
jerry_init(JERRY_INIT_EMPTY);
|
||||
|
||||
s_app_window_stack_get_top_window = (Window){};
|
||||
s_context = (GContext){};
|
||||
s_app_state_get_graphics_context = &s_context;
|
||||
s_app_event_loop_callback = NULL;
|
||||
|
||||
s_graphics_context_set_stroke_color = (MockCallRecordings){0};
|
||||
s_graphics_context_set_stroke_width = (MockCallRecordings){0};
|
||||
s_graphics_context_set_fill_color = (MockCallRecordings){0};
|
||||
s_graphics_fill_rect = (MockCallRecordings){0};
|
||||
s_graphics_fill_rect__color = GColorClear;
|
||||
s_graphics_draw_rect_precise = (MockCallRecordings){0};
|
||||
s_graphics_fill_radial_precise_internal = (MockCallRecordings){0};
|
||||
s_layer_mark_dirty = (MockCallRecordings){0};
|
||||
s_fonts_get_system_font = (MockCallRecordings){0};
|
||||
s_graphics_draw_text = (MockCallRecordings){0};
|
||||
s_graphics_text_attributes_destroy = (MockCallRecordings){0};
|
||||
s_graphics_text_layout_get_max_used_size = (MockCallRecordings){0};
|
||||
s_graphics_text_layout_get_max_used_size__result = (GSize){0};
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__cleanup(void) {
|
||||
fake_app_timer_deinit();
|
||||
|
||||
// some tests deinitialize the engine, avoid double de-init
|
||||
if (app_state_get_rocky_runtime_context() != NULL) {
|
||||
jerry_cleanup();
|
||||
rocky_runtime_context_deinit();
|
||||
}
|
||||
}
|
||||
|
||||
static const RockyGlobalAPI *s_graphics_api[] = {
|
||||
&GRAPHIC_APIS,
|
||||
NULL,
|
||||
};
|
||||
|
||||
extern RockyAPITextState s_rocky_text_state;
|
||||
|
||||
void test_rocky_api_graphics__handles_text_state(void) {
|
||||
cl_assert_equal_i(0, s_fonts_get_system_font.call_count);
|
||||
cl_assert_equal_i(0, s_graphics_text_attributes_destroy.call_count);
|
||||
rocky_global_init(s_graphics_api);
|
||||
cl_assert_equal_i(1, s_fonts_get_system_font.call_count);
|
||||
cl_assert_equal_i(0, s_graphics_text_attributes_destroy.call_count);
|
||||
rocky_global_deinit();
|
||||
cl_assert_equal_i(1, s_fonts_get_system_font.call_count);
|
||||
cl_assert_equal_i(0, s_graphics_text_attributes_destroy.call_count);
|
||||
|
||||
s_rocky_text_state.text_attributes = (GTextAttributes *)123;
|
||||
rocky_global_deinit();
|
||||
cl_assert_equal_i(1, s_fonts_get_system_font.call_count);
|
||||
cl_assert_equal_i(1, s_graphics_text_attributes_destroy.call_count);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__request_draw(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
cl_assert_equal_i(0, s_layer_mark_dirty.call_count);
|
||||
EXECUTE_SCRIPT("_rocky.requestDraw();");
|
||||
cl_assert_equal_i(1, s_layer_mark_dirty.call_count);
|
||||
cl_assert_equal_p(&s_app_window_stack_get_top_window.layer, s_layer_mark_dirty.last_call.layer);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__provides_draw_event(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
cl_assert_equal_b(false, rocky_global_has_event_handlers("draw"));
|
||||
EXECUTE_SCRIPT("_rocky.on('draw', function() {});");
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("draw"));
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__draw_event_has_ctx(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"var event = null;\n"
|
||||
"_rocky.on('draw', function(e) {event = e;});"
|
||||
);
|
||||
|
||||
const jerry_value_t event_null = prv_js_global_get_value("event");
|
||||
cl_assert_equal_b(true, jerry_value_is_null(event_null));
|
||||
jerry_release_value(event_null);
|
||||
|
||||
Layer *l = &app_window_stack_get_top_window()->layer;
|
||||
l->update_proc(l, NULL);
|
||||
const jerry_value_t event = prv_js_global_get_value("event");
|
||||
cl_assert_equal_b(true, jerry_value_is_object(event));
|
||||
|
||||
const jerry_value_t context_2d = jerry_get_object_field(event, "context");
|
||||
cl_assert_equal_b(true, jerry_value_is_object(context_2d));
|
||||
jerry_release_value(context_2d);
|
||||
jerry_release_value(event);
|
||||
}
|
||||
|
||||
jerry_value_t prv_create_canvas_context_2d_for_layer(Layer *layer);
|
||||
void layer_get_unobstructed_bounds(const Layer *layer, GRect *bounds_out) {
|
||||
*bounds_out = GRect(5, 6, 7, 8);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__canvas_offers_size(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
Layer l = {.bounds = GRect(1, 2, 3, 4)};
|
||||
const jerry_value_t ctx = prv_create_canvas_context_2d_for_layer(&l);
|
||||
jerry_set_object_field(jerry_get_global_object(), "ctx", ctx);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"var w = ctx.canvas.clientWidth;\n"
|
||||
"var h = ctx.canvas.clientHeight;\n"
|
||||
"var uol = ctx.canvas.unobstructedLeft;\n"
|
||||
"var uot = ctx.canvas.unobstructedTop;\n"
|
||||
"var uow = ctx.canvas.unobstructedWidth;\n"
|
||||
"var uoh = ctx.canvas.unobstructedHeight;\n"
|
||||
);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("w", 3);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("h", 4);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("uol", 5);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("uot", 6);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("uow", 7);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("uoh", 8);
|
||||
}
|
||||
|
||||
static const jerry_value_t prv_global_init_and_set_ctx(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
// make this easily testable by putting it int JS context as global
|
||||
Layer l = {.bounds = GRect(0, 0, 144, 168)};
|
||||
const jerry_value_t ctx = prv_create_canvas_context_2d_for_layer(&l);
|
||||
cl_assert_equal_b(jerry_value_is_object(ctx), true);
|
||||
jerry_set_object_field(jerry_get_global_object(), "ctx", ctx);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__drawing_rects(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
s_context.draw_state.fill_color = GColorJaegerGreen;
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.clearRect(1, 2, 3, 4);\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(1, s_graphics_fill_rect.call_count);
|
||||
cl_assert_equal_rect(GRect(1, 2, 3, 4), s_graphics_fill_rect.last_call.rect);
|
||||
cl_assert_equal_i(GColorBlackARGB8, s_graphics_fill_rect__color.argb);
|
||||
cl_assert_equal_i(GColorJaegerGreenARGB8, s_context.draw_state.fill_color.argb);
|
||||
|
||||
s_graphics_fill_rect = (MockCallRecordings){};
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.fillRect(5, 6, 7, 8);\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(1, s_graphics_fill_rect.call_count);
|
||||
cl_assert_equal_rect(GRect(5, 6, 7, 8), s_graphics_fill_rect.last_call.rect);
|
||||
|
||||
s_graphics_draw_rect_precise = (MockCallRecordings){};
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.strokeRect(9, 10.2, 11.5, 12.8);\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(1, s_graphics_draw_rect_precise.call_count);
|
||||
GRectPrecise expected_rect = {(int)(8.5*8), 78, (int)(11.5*8), (int)(12.8*8)};
|
||||
cl_assert_equal_rect_precise(expected_rect, s_graphics_draw_rect_precise.last_call.prect);
|
||||
}
|
||||
|
||||
#define PP(x, y) (GPointPrecise( \
|
||||
(int16_t)((x) * FIXED_S16_3_FACTOR), \
|
||||
(int16_t)((y) * FIXED_S16_3_FACTOR)))
|
||||
|
||||
void test_rocky_api_graphics__fill_radial(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.rockyFillRadial(30, 40, 10, 20, 0, Math.PI);\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(1, s_graphics_fill_radial_precise_internal.call_count);
|
||||
MockCallRecording *const lc = &s_graphics_fill_radial_precise_internal.last_call;
|
||||
cl_assert_equal_point_precise(PP(29.5, 39.5), lc->fill_radial_precise.center);
|
||||
cl_assert_equal_i(10*8, lc->fill_radial_precise.radius_inner.raw_value);
|
||||
cl_assert_equal_i(20*8, lc->fill_radial_precise.radius_outer.raw_value);
|
||||
cl_assert_equal_i(TRIG_MAX_ANGLE * 1 / 4, lc->fill_radial_precise.angle_start);
|
||||
cl_assert_equal_i(TRIG_MAX_ANGLE * 3 / 4, lc->fill_radial_precise.angle_end);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.rockyFillRadial(30, 40, 10, 30, 0, 2 * Math.PI);\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(2, s_graphics_fill_radial_precise_internal.call_count);
|
||||
cl_assert_equal_point_precise(PP(29.5, 39.5), lc->fill_radial_precise.center);
|
||||
cl_assert_equal_i(10*8, lc->fill_radial_precise.radius_inner.raw_value);
|
||||
cl_assert_equal_i(30*8, lc->fill_radial_precise.radius_outer.raw_value);
|
||||
cl_assert_equal_i(TRIG_MAX_ANGLE * 1 / 4, lc->fill_radial_precise.angle_start);
|
||||
cl_assert_equal_i(TRIG_MAX_ANGLE * 5 / 4, lc->fill_radial_precise.angle_end);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.rockyFillRadial(30.5, 40.1, 30, 10, 0, 2 * Math.PI);\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(3, s_graphics_fill_radial_precise_internal.call_count);
|
||||
cl_assert_equal_point_precise(PP(30, 39.625), lc->fill_radial_precise.center);
|
||||
cl_assert_equal_i(10*8, lc->fill_radial_precise.radius_inner.raw_value);
|
||||
cl_assert_equal_i(30*8, lc->fill_radial_precise.radius_outer.raw_value);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__fill_radial_not_enough_args(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR(
|
||||
"ctx.rockyFillRadial(30, 40, 10, 20, 0);\n",
|
||||
"TypeError: Not enough arguments"
|
||||
);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__fill_radial_type_error(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR(
|
||||
"ctx.rockyFillRadial(30, 40, 10, 20, 0, false);\n",
|
||||
"TypeError: Argument at index 5 is not a Number"
|
||||
);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__fill_radial_range_check(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR(
|
||||
"ctx.rockyFillRadial(4096, 40, 10, 20, 0, false);\n",
|
||||
"TypeError: Argument at index 0 is invalid: Value out of bounds for native type"
|
||||
);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__fill_radial_zero_radius(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
// inner radius = 0
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.rockyFillRadial(30, 40, 0, 20, 0, Math.PI);\n"
|
||||
);
|
||||
MockCallRecording *const lc = &s_graphics_fill_radial_precise_internal.last_call;
|
||||
cl_assert_equal_i(1, s_graphics_fill_radial_precise_internal.call_count);
|
||||
cl_assert_equal_point_precise((PP(29.5, 39.5)), lc->fill_radial_precise.center);
|
||||
cl_assert_equal_i(0, lc->fill_radial_precise.radius_inner.raw_value);
|
||||
cl_assert_equal_i(20 * 8, lc->fill_radial_precise.radius_outer.raw_value);
|
||||
|
||||
// inner radius capped to >= 0
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.rockyFillRadial(30, 40, -10, 20, 0, Math.PI);\n"
|
||||
);
|
||||
cl_assert_equal_i(2, s_graphics_fill_radial_precise_internal.call_count);
|
||||
cl_assert_equal_point_precise((PP(29.5, 39.5)), lc->fill_radial_precise.center);
|
||||
cl_assert_equal_i(0, lc->fill_radial_precise.radius_inner.raw_value);
|
||||
cl_assert_equal_i(20 * 8, lc->fill_radial_precise.radius_outer.raw_value);
|
||||
|
||||
// outer radius capped to >= 0
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.rockyFillRadial(30, 40, -10, -20, 0, Math.PI);\n"
|
||||
);
|
||||
cl_assert_equal_i(3, s_graphics_fill_radial_precise_internal.call_count);
|
||||
cl_assert_equal_point_precise((PP(29.5, 39.5)), lc->fill_radial_precise.center);
|
||||
cl_assert_equal_i(0, lc->fill_radial_precise.radius_inner.raw_value);
|
||||
cl_assert_equal_i(0, lc->fill_radial_precise.radius_outer.raw_value);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__line_styles(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.lineWidth = 8;\n"
|
||||
"var w = ctx.lineWidth;\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(1, s_graphics_context_set_stroke_width.call_count);
|
||||
cl_assert_equal_i(8, s_graphics_context_set_stroke_width.last_call.width);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("w", s_graphics_context_set_stroke_width.last_call.width);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.lineWidth = 2.1;\n"
|
||||
"var w = ctx.lineWidth;\n"
|
||||
);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("w", 2);
|
||||
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR(
|
||||
"ctx.lineWidth = -4;\n",
|
||||
"TypeError: Argument at index 0 is invalid: Value out of bounds for native type"
|
||||
);
|
||||
EXECUTE_SCRIPT("var w = ctx.lineWidth;\n");
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("w", 2);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__line_styles_check_bounds(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR(
|
||||
"ctx.lineWidth = -1;",
|
||||
"TypeError: Argument at index 0 is invalid: Value out of bounds for native type"
|
||||
);
|
||||
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR(
|
||||
"ctx.lineWidth = 256;",
|
||||
"TypeError: Argument at index 0 is invalid: Value out of bounds for native type"
|
||||
);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__fill_and_stroke_styles(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.fillStyle = '#f00';\n"
|
||||
"ctx.strokeStyle = 'white';\n"
|
||||
"var c = ctx.fillStyle;\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(1, s_graphics_context_set_fill_color.call_count);
|
||||
cl_assert_equal_i(GColorRedARGB8, s_graphics_context_set_fill_color.last_call.color.argb);
|
||||
cl_assert_equal_i(1, s_graphics_context_set_stroke_color.call_count);
|
||||
cl_assert_equal_i(GColorWhiteARGB8, s_graphics_context_set_stroke_color.last_call.color.argb);
|
||||
|
||||
// ignores invalid values
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.fillStyle = 'unknown';\n"
|
||||
"ctx.strokeStyle = '4%2F';\n"
|
||||
);
|
||||
cl_assert_equal_i(1, s_graphics_context_set_fill_color.call_count);
|
||||
cl_assert_equal_i(1, s_graphics_context_set_stroke_color.call_count);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__canvas_state(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
// calling restore if nothing was stored is a no-op
|
||||
s_context.draw_state.fill_color.argb = 1;
|
||||
EXECUTE_SCRIPT("ctx.restore()\n");
|
||||
cl_assert_equal_i(1, s_context.draw_state.fill_color.argb);
|
||||
|
||||
EXECUTE_SCRIPT("ctx.save()\n"); // 1
|
||||
s_context.draw_state.fill_color.argb = 2;
|
||||
EXECUTE_SCRIPT("ctx.save()\n"); // 2
|
||||
s_context.draw_state.fill_color.argb = 3;
|
||||
|
||||
EXECUTE_SCRIPT("ctx.restore()\n"); // -> 2 (one element left)
|
||||
cl_assert_equal_i(2, s_context.draw_state.fill_color.argb);
|
||||
|
||||
EXECUTE_SCRIPT("ctx.restore()\n"); // -> 1 (no element left)
|
||||
cl_assert_equal_i(1, s_context.draw_state.fill_color.argb);
|
||||
|
||||
EXECUTE_SCRIPT("ctx.restore()\n"); // no-op
|
||||
cl_assert_equal_i(1, s_context.draw_state.fill_color.argb);
|
||||
}
|
||||
|
||||
static const int16_t large_int = 10000;
|
||||
|
||||
void test_rocky_api_graphics__fill_text(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
// we do this in C and not JS as color binding is not linked in this unit-test
|
||||
// what we want to test though is that the text color is taken from fill color
|
||||
rocky_api_graphics_get_gcontext()->draw_state.fill_color = GColorRed;
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.fillText('some text', 10, 10);\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(1, s_graphics_draw_text.call_count);
|
||||
cl_assert_equal_s("some text", s_graphics_draw_text.last_call.draw_text.text);
|
||||
cl_assert_equal_i(GColorRedARGB8, s_graphics_draw_text.last_call.draw_text.color.argb);
|
||||
cl_assert_equal_rect((GRect(10, 10, large_int, large_int)),
|
||||
s_graphics_draw_text.last_call.draw_text.box);
|
||||
|
||||
rocky_api_graphics_get_gcontext()->draw_state.fill_color = GColorBlue;
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.fillText('more text', -10.5, 5000, 60);\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(2, s_graphics_draw_text.call_count);
|
||||
cl_assert_equal_s("more text", s_graphics_draw_text.last_call.draw_text.text);
|
||||
cl_assert_equal_i(GColorBlueARGB8, s_graphics_draw_text.last_call.draw_text.color.argb);
|
||||
cl_assert_equal_rect((GRect(-11, 5000, 60, large_int)),
|
||||
s_graphics_draw_text.last_call.draw_text.box);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__fill_text_coordinates(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
EXECUTE_SCRIPT("ctx.fillText('some text', 0, 1.5);");
|
||||
cl_assert_equal_rect((GRect(0, 2, large_int, large_int)),
|
||||
s_graphics_draw_text.last_call.draw_text.box);
|
||||
|
||||
EXECUTE_SCRIPT("ctx.fillText('some text', -0.2, 1.2, 10.5);");
|
||||
cl_assert_equal_rect((GRect(0, 1, 11, large_int)),
|
||||
s_graphics_draw_text.last_call.draw_text.box);
|
||||
|
||||
EXECUTE_SCRIPT("ctx.fillText('some text', -0.5, 1.2, -0.5);");
|
||||
cl_assert_equal_rect((GRect(-1, 1, -1, large_int)),
|
||||
s_graphics_draw_text.last_call.draw_text.box);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__fill_text_aligned(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
// we do this in C and not JS as color binding is not linked in this unit-test
|
||||
// what we want to test though is that the text color is taken from fill color
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.textAlign = 'left';\n"
|
||||
"ctx.fillText('some text', 100, 100);\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(1, s_graphics_draw_text.call_count);
|
||||
cl_assert_equal_rect((GRect(100, 100, large_int, large_int)),
|
||||
s_graphics_draw_text.last_call.draw_text.box);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.textAlign = 'center';\n"
|
||||
"ctx.fillText('some text', 100, 100);\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(2, s_graphics_draw_text.call_count);
|
||||
cl_assert_equal_rect((GRect(-4900, 100, large_int, large_int)),
|
||||
s_graphics_draw_text.last_call.draw_text.box);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.textAlign = 'right';\n"
|
||||
"ctx.fillText('some text', 100, 100);\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(3, s_graphics_draw_text.call_count);
|
||||
cl_assert_equal_rect((GRect(-9900, 100, large_int, large_int)),
|
||||
s_graphics_draw_text.last_call.draw_text.box);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__text_align(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
// intial value
|
||||
cl_assert_equal_i(GTextAlignmentLeft, s_rocky_text_state.alignment);
|
||||
|
||||
s_rocky_text_state.alignment = (GTextAlignment)-1;
|
||||
// unsupported values don't change the value
|
||||
EXECUTE_SCRIPT("ctx.textAlign = 123;\n");
|
||||
cl_assert_equal_i(-1, s_rocky_text_state.alignment);
|
||||
EXECUTE_SCRIPT("ctx.textAlign = 'unknown';\n");
|
||||
cl_assert_equal_i(-1, s_rocky_text_state.alignment);
|
||||
|
||||
|
||||
EXECUTE_SCRIPT("ctx.textAlign = 'left';\nvar a = ctx.textAlign;\n");
|
||||
cl_assert_equal_i(GTextAlignmentLeft, s_rocky_text_state.alignment);
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("a", "left");
|
||||
|
||||
EXECUTE_SCRIPT("ctx.textAlign = 'right';\nvar a = ctx.textAlign;\n");
|
||||
cl_assert_equal_i(GTextAlignmentRight, s_rocky_text_state.alignment);
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("a", "right");
|
||||
|
||||
EXECUTE_SCRIPT("ctx.textAlign = 'center';\nvar a = ctx.textAlign;\n");
|
||||
cl_assert_equal_i(GTextAlignmentCenter, s_rocky_text_state.alignment);
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("a", "center");
|
||||
|
||||
// we only support LTR
|
||||
EXECUTE_SCRIPT("ctx.textAlign = 'start';\nvar a = ctx.textAlign;\n");
|
||||
cl_assert_equal_i(GTextAlignmentLeft, s_rocky_text_state.alignment);
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("a", "left");
|
||||
|
||||
EXECUTE_SCRIPT("ctx.textAlign = 'end';\nvar a = ctx.textAlign;\n");
|
||||
cl_assert_equal_i(GTextAlignmentRight, s_rocky_text_state.alignment);
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("a", "right");
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__text_font(void) {
|
||||
cl_assert_equal_i(0, s_fonts_get_system_font.call_count);
|
||||
s_fonts_get_system_font__result = (GFont)123;
|
||||
rocky_global_init(s_graphics_api);
|
||||
cl_assert_equal_i(1, s_fonts_get_system_font.call_count);
|
||||
cl_assert_equal_p((GFont)123, s_rocky_text_state.font);
|
||||
|
||||
// make this easily testable by putting it int JS context as global
|
||||
Layer l = {.bounds = GRect(0, 0, 144, 168)};
|
||||
const jerry_value_t ctx = prv_create_canvas_context_2d_for_layer(&l);
|
||||
jerry_set_object_field(jerry_get_global_object(), "ctx", ctx);
|
||||
|
||||
|
||||
s_rocky_text_state.font = (GFont)-1;
|
||||
// unsupported values don't change the value
|
||||
EXECUTE_SCRIPT("ctx.font = 123;\n");
|
||||
cl_assert_equal_p((GFont)-1, s_rocky_text_state.font);
|
||||
EXECUTE_SCRIPT("ctx.font = 'unknown';\n");
|
||||
cl_assert_equal_p((GFont)-1, s_rocky_text_state.font);
|
||||
cl_assert_equal_i(1, s_fonts_get_system_font.call_count);
|
||||
|
||||
EXECUTE_SCRIPT("ctx.font = '14px bold Gothic';\n");
|
||||
cl_assert_equal_i(2, s_fonts_get_system_font.call_count);
|
||||
cl_assert_equal_p(FONT_KEY_GOTHIC_14_BOLD, s_fonts_get_system_font.last_call.font_key);
|
||||
|
||||
EXECUTE_SCRIPT("ctx.font = '28px Gothic';\nvar f = ctx.font;\n");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("f", "28px Gothic");
|
||||
}
|
||||
|
||||
extern T_STATIC void prv_graphics_color_to_char_buffer(GColor8 color, char *buf_out);
|
||||
|
||||
#define TEST_COLOR_STRING(gcolor, expect_str) do { \
|
||||
char buf[12]; \
|
||||
prv_graphics_color_to_char_buffer(gcolor, buf); \
|
||||
cl_assert_equal_s(buf, expect_str); \
|
||||
} while(0);
|
||||
|
||||
void test_rocky_api_graphics__color_names(void) {
|
||||
TEST_COLOR_STRING(GColorClear, "transparent");
|
||||
TEST_COLOR_STRING((GColor){ .a = 1 }, "transparent");
|
||||
TEST_COLOR_STRING(GColorRed, "#FF0000");
|
||||
TEST_COLOR_STRING(GColorMalachite, "#00FF55");
|
||||
}
|
||||
|
||||
extern T_STATIC const RockyAPISystemFontDefinition s_font_definitions[];
|
||||
bool prv_font_definition_from_value(jerry_value_t value, RockyAPISystemFontDefinition **result);
|
||||
|
||||
void test_rocky_api_graphics__text_font_names_unique(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
const RockyAPISystemFontDefinition *def = s_font_definitions;
|
||||
while (def->js_name) {
|
||||
const jerry_value_t name_js = jerry_create_string((jerry_char_t *)def->js_name);
|
||||
RockyAPISystemFontDefinition *cmp_def = NULL;
|
||||
bool actual = prv_font_definition_from_value(name_js, &cmp_def);
|
||||
cl_assert_equal_b(true, actual);
|
||||
cl_assert_equal_s(cmp_def->res_key, def->res_key);
|
||||
jerry_release_value(name_js);
|
||||
def++;
|
||||
}
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__measure_text(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
// fill text_state with unique values we can test against
|
||||
s_rocky_text_state = (RockyAPITextState) {
|
||||
.font = (GFont)-1,
|
||||
.overflow_mode = (GTextOverflowMode)-2,
|
||||
.alignment = (GTextAlignment)-3,
|
||||
.text_attributes = (GTextAttributes *)-4,
|
||||
};
|
||||
|
||||
s_graphics_text_layout_get_max_used_size__result = GSize(123, 456);
|
||||
EXECUTE_SCRIPT(
|
||||
"var tm = ctx.measureText('foo');\n"
|
||||
"var tm_w = tm.width;\n"
|
||||
"var tm_h = tm.height;\n"
|
||||
);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("tm_w", 123);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("tm_h", 456);
|
||||
|
||||
cl_assert_equal_i(1, s_graphics_text_layout_get_max_used_size.call_count);
|
||||
const MockCallRecording *lc = &s_graphics_text_layout_get_max_used_size.last_call;
|
||||
cl_assert_equal_s("foo", lc->max_used_size.text);
|
||||
cl_assert_equal_p(s_rocky_text_state.font, lc->max_used_size.font);
|
||||
cl_assert_equal_rect((GRect(0, 0, INT16_MAX, INT16_MAX)), lc->max_used_size.box);
|
||||
cl_assert_equal_i(s_rocky_text_state.overflow_mode, lc->max_used_size.overflow_mode);
|
||||
cl_assert_equal_i(s_rocky_text_state.alignment, lc->max_used_size.alignment);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__state_initialized_between_renders(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
// fill text_state with unique values we can test against
|
||||
s_rocky_text_state = (RockyAPITextState) {
|
||||
.font = (GFont)-1,
|
||||
.overflow_mode = (GTextOverflowMode)-2,
|
||||
.alignment = (GTextAlignment)-3,
|
||||
.text_attributes = (GTextAttributes *)-4,
|
||||
};
|
||||
|
||||
EXECUTE_SCRIPT("_rocky.on('draw', function(e) {});");
|
||||
Layer *l = &app_window_stack_get_top_window()->layer;
|
||||
l->update_proc(l, NULL);
|
||||
|
||||
cl_assert_equal_i(1, s_fonts_get_system_font.call_count);
|
||||
cl_assert_equal_i(GTextAlignmentLeft, s_rocky_text_state.alignment);
|
||||
cl_assert_equal_i(GTextOverflowModeWordWrap, s_rocky_text_state.overflow_mode);
|
||||
cl_assert_equal_p(NULL, s_rocky_text_state.text_attributes);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics__context_2d_prototype_wrap_function(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
EXECUTE_SCRIPT("var origFillRect = _rocky.CanvasRenderingContext2D.prototype.fillRect;\n"
|
||||
"_rocky.CanvasRenderingContext2D.prototype.fillRect = function(x, y, w, h) {\n"
|
||||
" w *= 2;\n"
|
||||
" h *= 2;\n"
|
||||
" origFillRect.call(this, x, y, w, h);\n"
|
||||
"};\n"
|
||||
"ctx.fillRect(5, 6, 7, 8);\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(1, s_graphics_fill_rect.call_count);
|
||||
cl_assert_equal_rect(GRect(5, 6, 7 * 2, 8 * 2), s_graphics_fill_rect.last_call.rect);
|
||||
}
|
194
tests/fw/javascript/test_rocky_api_graphics_color.c
Normal file
194
tests/fw/javascript/test_rocky_api_graphics_color.c
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
#include "test_jerry_port_common.h"
|
||||
#include "test_rocky_common.h"
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/rockyjs/api/rocky_api_global.h"
|
||||
#include "applib/rockyjs/api/rocky_api_graphics.h"
|
||||
#include "applib/rockyjs/api/rocky_api_graphics_color.h"
|
||||
#include "applib/rockyjs/pbl_jerry_port.h"
|
||||
|
||||
// Standard
|
||||
#include "string.h"
|
||||
|
||||
// Fakes
|
||||
#include "fake_app_timer.h"
|
||||
#include "fake_time.h"
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_manager.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_sleep.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_sys_exit.h"
|
||||
|
||||
size_t heap_bytes_free(void) {
|
||||
return 123456;
|
||||
}
|
||||
|
||||
void layer_get_unobstructed_bounds(const Layer *layer, GRect *bounds_out) {
|
||||
*bounds_out = layer->bounds;
|
||||
}
|
||||
|
||||
static Window s_app_window_stack_get_top_window;
|
||||
Window *app_window_stack_get_top_window() {
|
||||
return &s_app_window_stack_get_top_window;
|
||||
}
|
||||
|
||||
GContext s_context;
|
||||
|
||||
// mocks
|
||||
static MockCallRecordings s_graphics_context_set_fill_color;
|
||||
void graphics_context_set_fill_color(GContext* ctx, GColor color) {
|
||||
record_mock_call(s_graphics_context_set_fill_color) {
|
||||
.ctx = ctx, .color = color,
|
||||
};
|
||||
}
|
||||
|
||||
static MockCallRecordings s_graphics_context_set_stroke_color;
|
||||
void graphics_context_set_stroke_color(GContext* ctx, GColor color) {
|
||||
record_mock_call(s_graphics_context_set_stroke_color) {
|
||||
.ctx = ctx, .color = color,
|
||||
};
|
||||
}
|
||||
|
||||
void graphics_context_set_stroke_width(GContext* ctx, uint8_t stroke_width) {}
|
||||
|
||||
void graphics_draw_line(GContext* ctx, GPoint p0, GPoint p1) {}
|
||||
|
||||
void graphics_fill_rect(GContext *ctx, const GRect *rect) {}
|
||||
|
||||
void graphics_fill_round_rect_by_value(GContext *ctx, GRect rect, uint16_t corner_radius,
|
||||
GCornerMask corner_mask) {}
|
||||
|
||||
void graphics_draw_rect_precise(GContext *ctx, const GRectPrecise *rect) {}
|
||||
|
||||
void graphics_fill_radial_precise_internal(GContext *ctx, GPointPrecise center,
|
||||
Fixed_S16_3 radius_inner, Fixed_S16_3 radius_outer,
|
||||
int32_t angle_start, int32_t angle_end) {}
|
||||
|
||||
void layer_mark_dirty(Layer *layer) {}
|
||||
|
||||
void rocky_api_graphics_path2d_add_canvas_methods(jerry_value_t obj) {}
|
||||
void rocky_api_graphics_path2d_cleanup(void) {}
|
||||
void rocky_api_graphics_path2d_reset_state(void) {}
|
||||
void rocky_api_graphics_text_init(void) {}
|
||||
void rocky_api_graphics_text_deinit(void) {}
|
||||
void rocky_api_graphics_text_add_canvas_methods(jerry_value_t obj) {}
|
||||
void rocky_api_graphics_text_reset_state(void) {}
|
||||
|
||||
jerry_value_t prv_rocky_api_graphics_get_canvas_context_2d(void);
|
||||
|
||||
void test_rocky_api_graphics_color__initialize(void) {
|
||||
fake_app_timer_init();
|
||||
rocky_runtime_context_init();
|
||||
jerry_init(JERRY_INIT_EMPTY);
|
||||
|
||||
s_app_window_stack_get_top_window = (Window){};
|
||||
s_context = (GContext){};
|
||||
s_app_state_get_graphics_context = &s_context;
|
||||
s_app_event_loop_callback = NULL;
|
||||
|
||||
s_graphics_context_set_stroke_color = (MockCallRecordings){0};
|
||||
s_graphics_context_set_fill_color = (MockCallRecordings){0};
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics_color__cleanup(void) {
|
||||
fake_app_timer_deinit();
|
||||
|
||||
// some tests deinitialize the engine, avoid double de-init
|
||||
if (app_state_get_rocky_runtime_context() != NULL) {
|
||||
jerry_cleanup();
|
||||
rocky_runtime_context_deinit();
|
||||
}
|
||||
}
|
||||
|
||||
static const RockyGlobalAPI *s_graphics_api[] = {
|
||||
&GRAPHIC_APIS,
|
||||
NULL,
|
||||
};
|
||||
|
||||
#define cl_assert_parsed_color(str, expected_color_ptr) do { \
|
||||
GColor actual_color = {0}; \
|
||||
actual_color.argb = 123; \
|
||||
const bool actual_bool = rocky_api_graphics_color_parse(str, &actual_color); \
|
||||
if (expected_color_ptr) { \
|
||||
cl_assert_equal_b(true, actual_bool); \
|
||||
cl_assert_equal_i(((GColor*)(expected_color_ptr))->argb, actual_color.argb); \
|
||||
} else { \
|
||||
cl_assert_equal_b(false, actual_bool); \
|
||||
}\
|
||||
} while(0)
|
||||
|
||||
|
||||
void test_rocky_api_graphics_color__parse_names(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
cl_assert_parsed_color("unknown", NULL);
|
||||
cl_assert_parsed_color("clear", &GColorClear);
|
||||
cl_assert_parsed_color("black", &GColorBlack);
|
||||
cl_assert_parsed_color("red", &GColorRed);
|
||||
cl_assert_parsed_color("white", &GColorWhite);
|
||||
cl_assert_parsed_color("gray", &GColorLightGray);
|
||||
}
|
||||
|
||||
extern const RockyAPIGraphicsColorDefinition s_color_definitions[];
|
||||
|
||||
|
||||
void test_rocky_api_graphics_color__color_names_consistent(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
const RockyAPIGraphicsColorDefinition *def = s_color_definitions;
|
||||
while (def->name) {
|
||||
GColor8 actual;
|
||||
const bool result = rocky_api_graphics_color_parse(def->name, &actual);
|
||||
cl_assert_equal_b(true, result);
|
||||
cl_assert_equal_i(def->value, actual.argb);
|
||||
def++;
|
||||
}
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics_color__hex(void) {
|
||||
// invalid cases
|
||||
cl_assert_parsed_color("#", NULL);
|
||||
cl_assert_parsed_color("##q3", NULL);
|
||||
cl_assert_parsed_color("", NULL);
|
||||
cl_assert_parsed_color("#00zz10", NULL);
|
||||
cl_assert_parsed_color("#123456789", NULL);
|
||||
|
||||
// different lengths
|
||||
cl_assert_parsed_color("#f00", &GColorRed);
|
||||
cl_assert_parsed_color("#FF0000", &GColorRed);
|
||||
cl_assert_parsed_color("#F00f", &GColorRed);
|
||||
cl_assert_parsed_color("#FF0000FF", &GColorRed);
|
||||
|
||||
// discard rgb components if alpha == 0
|
||||
cl_assert_parsed_color("#12345600", &GColorClear);
|
||||
cl_assert_parsed_color("#1230", &GColorClear);
|
||||
|
||||
// correctly assign different components
|
||||
cl_assert_parsed_color("#00FF00", &GColorGreen);
|
||||
cl_assert_parsed_color("#0000FF", &GColorBlue);
|
||||
|
||||
}
|
472
tests/fw/javascript/test_rocky_api_graphics_path2d.c
Normal file
472
tests/fw/javascript/test_rocky_api_graphics_path2d.c
Normal file
|
@ -0,0 +1,472 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <applib/rockyjs/api/rocky_api_graphics_path2d.h>
|
||||
#include "clar.h"
|
||||
#include "test_jerry_port_common.h"
|
||||
#include "test_rocky_common.h"
|
||||
|
||||
#include "applib/graphics/gpath.h"
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/rockyjs/api/rocky_api_global.h"
|
||||
#include "applib/rockyjs/api/rocky_api_graphics.h"
|
||||
#include "applib/rockyjs/api/rocky_api_graphics_color.h"
|
||||
#include "applib/rockyjs/pbl_jerry_port.h"
|
||||
#include "util/trig.h"
|
||||
|
||||
// Standard
|
||||
#include "string.h"
|
||||
|
||||
// Fakes
|
||||
#include "fake_app_timer.h"
|
||||
#include "fake_logging.h"
|
||||
#include "fake_pbl_malloc.h"
|
||||
#include "fake_time.h"
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_manager.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_sleep.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_sys_exit.h"
|
||||
|
||||
size_t heap_bytes_free(void) {
|
||||
return 123456;
|
||||
}
|
||||
|
||||
void layer_get_unobstructed_bounds(const Layer *layer, GRect *bounds_out) {
|
||||
*bounds_out = layer->bounds;
|
||||
}
|
||||
|
||||
bool rocky_api_graphics_color_parse(const char *color_value, GColor8 *parsed_color) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rocky_api_graphics_color_from_value(jerry_value_t value, GColor *result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static Window s_app_window_stack_get_top_window;
|
||||
Window *app_window_stack_get_top_window() {
|
||||
return &s_app_window_stack_get_top_window;
|
||||
}
|
||||
|
||||
GPointPrecise gpoint_from_polar_precise(const GPointPrecise *precise_center,
|
||||
uint16_t precise_radius, int32_t angle) {
|
||||
return GPointPreciseFromGPoint(GPointZero);
|
||||
}
|
||||
|
||||
GContext s_context;
|
||||
|
||||
void rocky_api_graphics_text_init(void) {}
|
||||
void rocky_api_graphics_text_deinit(void) {}
|
||||
void rocky_api_graphics_text_add_canvas_methods(jerry_value_t obj) {}
|
||||
void rocky_api_graphics_text_reset_state(void) {}
|
||||
|
||||
void graphics_context_set_fill_color(GContext* ctx, GColor color) {}
|
||||
void graphics_context_set_stroke_color(GContext* ctx, GColor color) {}
|
||||
void graphics_context_set_stroke_width(GContext* ctx, uint8_t stroke_width) {}
|
||||
|
||||
// mocks
|
||||
|
||||
|
||||
static MockCallRecordings s_graphics_line_draw_precise_stroked;
|
||||
void graphics_line_draw_precise_stroked(GContext* ctx, GPointPrecise p0, GPointPrecise p1) {
|
||||
record_mock_call(s_graphics_line_draw_precise_stroked) {
|
||||
.ctx = ctx, .pp0 = p0, .pp1 = p1,
|
||||
};
|
||||
}
|
||||
|
||||
void graphics_draw_line(GContext* ctx, GPoint p0, GPoint p1) {
|
||||
// TODO: remove me PBL-42458 (still used for drawing arc)
|
||||
record_mock_call(s_graphics_line_draw_precise_stroked) {.ctx = ctx};
|
||||
}
|
||||
|
||||
MockCallRecordings s_graphics_draw_arc_precise;
|
||||
void graphics_draw_arc_precise_internal(GContext *ctx, GPointPrecise center, Fixed_S16_3 radius,
|
||||
int32_t angle_start, int32_t angle_end) {
|
||||
record_mock_call(s_graphics_draw_arc_precise) {
|
||||
.draw_arc.center = center,
|
||||
.draw_arc.radius = radius,
|
||||
.draw_arc.angle_start = angle_start,
|
||||
.draw_arc.angle_end = angle_end,
|
||||
};
|
||||
}
|
||||
|
||||
MockCallRecordings s_gpath_draw_filled;
|
||||
void gpath_draw_filled(GContext* ctx, GPath *path) {
|
||||
record_mock_call(s_gpath_draw_filled) {
|
||||
.path.num_points = path->num_points,
|
||||
};
|
||||
memcpy(s_gpath_draw_filled.last_call.path.points,
|
||||
path->points, sizeof(path->points[0]) * path->num_points);
|
||||
}
|
||||
|
||||
|
||||
void graphics_fill_rect(GContext *ctx, const GRect *rect) {}
|
||||
|
||||
void graphics_fill_round_rect_by_value(GContext *ctx, GRect rect, uint16_t corner_radius,
|
||||
GCornerMask corner_mask) {}
|
||||
|
||||
void graphics_draw_rect_precise(GContext *ctx, const GRectPrecise *rect) {}
|
||||
|
||||
void graphics_fill_radial_precise_internal(GContext *ctx, GPointPrecise center,
|
||||
Fixed_S16_3 radius_inner, Fixed_S16_3 radius_outer,
|
||||
int32_t angle_start, int32_t angle_end) {}
|
||||
|
||||
void layer_mark_dirty(Layer *layer) {}
|
||||
|
||||
jerry_value_t prv_create_canvas_context_2d_for_layer(Layer *layer);
|
||||
static void prv_create_global_ctx(void) {
|
||||
// make this easily testable by putting it int JS context as global
|
||||
Layer l = {.bounds = GRect(0, 0, 144, 168)};
|
||||
const jerry_value_t ctx = prv_create_canvas_context_2d_for_layer(&l);
|
||||
cl_assert_equal_b(jerry_value_is_object(ctx), true);
|
||||
jerry_set_object_field(jerry_get_global_object(), "ctx", ctx);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics_path2d__initialize(void) {
|
||||
fake_malloc_set_largest_free_block(~0);
|
||||
s_log_internal__expected = NULL;
|
||||
|
||||
rocky_runtime_context_init();
|
||||
fake_app_timer_init();
|
||||
jerry_init(JERRY_INIT_EMPTY);
|
||||
|
||||
s_app_window_stack_get_top_window = (Window){};
|
||||
s_context = (GContext){};
|
||||
s_app_state_get_graphics_context = &s_context;
|
||||
s_app_event_loop_callback = NULL;
|
||||
|
||||
s_graphics_line_draw_precise_stroked = (MockCallRecordings){0};
|
||||
s_graphics_draw_arc_precise = (MockCallRecordings){0};
|
||||
s_gpath_draw_filled = (MockCallRecordings){0};
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics_path2d__cleanup(void) {
|
||||
fake_app_timer_deinit();
|
||||
|
||||
// Frees the internal path steps array ():
|
||||
rocky_api_graphics_path2d_reset_state();
|
||||
|
||||
// some tests deinitialize the engine, avoid double de-init
|
||||
if (app_state_get_rocky_runtime_context() != NULL) {
|
||||
jerry_cleanup();
|
||||
rocky_runtime_context_deinit();
|
||||
}
|
||||
|
||||
fake_pbl_malloc_check_net_allocs();
|
||||
}
|
||||
|
||||
static const RockyGlobalAPI *s_graphics_api[] = {
|
||||
&GRAPHIC_APIS,
|
||||
NULL,
|
||||
};
|
||||
|
||||
#define PP(x, y) \
|
||||
GPointPrecise((int16_t)(((x)) * FIXED_S16_3_FACTOR), \
|
||||
(int16_t)(((y)) * FIXED_S16_3_FACTOR))
|
||||
|
||||
void test_rocky_api_graphics_path2d__invalid_coords(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
prv_create_global_ctx();
|
||||
|
||||
EXECUTE_SCRIPT("ctx.moveTo(4095.375, -4095.5);");
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR("ctx.moveTo(4096.5, 0);", "TypeError: Value out of bounds");
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR("ctx.moveTo(0, -4095.625);", "TypeError: Value out of bounds");
|
||||
|
||||
EXECUTE_SCRIPT("ctx.lineTo(4095.375, -4095.5);");
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR("ctx.lineTo(4096.5, 0);", "TypeError: Value out of bounds");
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR("ctx.lineTo(0, -4095.625);", "TypeError: Value out of bounds");
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics_path2d__minimal_path(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
prv_create_global_ctx();
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.beginPath();\n"
|
||||
"ctx.moveTo(1, 2);\n"
|
||||
"ctx.lineTo(3.5, -4.5);\n"
|
||||
"ctx.stroke();\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(1, s_graphics_line_draw_precise_stroked.call_count);
|
||||
cl_assert_equal_point_precise(PP(0.5, 1.5), s_graphics_line_draw_precise_stroked.last_call.pp0);
|
||||
cl_assert_equal_point_precise(PP(3, -5), s_graphics_line_draw_precise_stroked.last_call.pp1);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.fill();\n"
|
||||
);
|
||||
cl_assert_equal_i(0, s_gpath_draw_filled.call_count);
|
||||
}
|
||||
|
||||
|
||||
void test_rocky_api_graphics_path2d__more_lines(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
prv_create_global_ctx();
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.beginPath();\n"
|
||||
"ctx.moveTo(1, 2);\n"
|
||||
"ctx.lineTo(3, 4);\n"
|
||||
"ctx.lineTo(5, 6);\n"
|
||||
"ctx.lineTo(7, 8);\n"
|
||||
"ctx.moveTo(9, 10);\n"
|
||||
"ctx.lineTo(11, 12);\n"
|
||||
"ctx.stroke();\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(4, s_graphics_line_draw_precise_stroked.call_count);
|
||||
cl_assert_equal_point_precise(PP(8.5, 9.5), s_graphics_line_draw_precise_stroked.last_call.pp0);
|
||||
cl_assert_equal_point_precise(PP(10.5, 11.5), s_graphics_line_draw_precise_stroked.last_call.pp1);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.fill();\n"
|
||||
);
|
||||
// only first shape has at least 3 points
|
||||
cl_assert_equal_i(1, s_gpath_draw_filled.call_count);
|
||||
MockCallRecording *lc = &s_gpath_draw_filled.last_call;
|
||||
cl_assert_equal_i(4, lc->path.num_points);
|
||||
cl_assert_equal_point(GPoint(0, 1), lc->path.points[0]);
|
||||
cl_assert_equal_point(GPoint(2, 3), lc->path.points[1]);
|
||||
cl_assert_equal_point(GPoint(4, 5), lc->path.points[2]);
|
||||
cl_assert_equal_point(GPoint(6, 7), lc->path.points[3]);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics_path2d__fill(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
prv_create_global_ctx();
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.moveTo(1, 2);\n"
|
||||
"ctx.lineTo(3, 4);\n"
|
||||
"ctx.fill();\n"
|
||||
);
|
||||
// only 2 points
|
||||
cl_assert_equal_i(0, s_gpath_draw_filled.call_count);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.lineTo(5, 6);\n"
|
||||
"ctx.fill();\n"
|
||||
);
|
||||
cl_assert_equal_i(1, s_gpath_draw_filled.call_count);
|
||||
MockCallRecording *lc = &s_gpath_draw_filled.last_call;
|
||||
cl_assert_equal_i(3, lc->path.num_points);
|
||||
cl_assert_equal_point(GPoint(0, 1), lc->path.points[0]);
|
||||
cl_assert_equal_point(GPoint(2, 3), lc->path.points[1]);
|
||||
cl_assert_equal_point(GPoint(4, 5), lc->path.points[2]);
|
||||
|
||||
s_gpath_draw_filled.call_count = 0;
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.moveTo(7, 8);\n"
|
||||
"ctx.lineTo(9, 10);\n"
|
||||
"ctx.fill();\n"
|
||||
);
|
||||
|
||||
// still only the first part (before the .moveTo()) as the second only has two points
|
||||
cl_assert_equal_i(1, s_gpath_draw_filled.call_count);
|
||||
cl_assert_equal_i(3, lc->path.num_points);
|
||||
cl_assert_equal_point(GPoint(0, 1), lc->path.points[0]);
|
||||
cl_assert_equal_point(GPoint(2, 3), lc->path.points[1]);
|
||||
cl_assert_equal_point(GPoint(4, 5), lc->path.points[2]);
|
||||
|
||||
s_gpath_draw_filled.call_count = 0;
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.lineTo(11.5, 12.7);\n"
|
||||
"ctx.fill();\n"
|
||||
);
|
||||
// still only the first part (before the .moveTo()) as the second only has two points
|
||||
cl_assert_equal_i(2, s_gpath_draw_filled.call_count);
|
||||
cl_assert_equal_i(3, lc->path.num_points);
|
||||
cl_assert_equal_point(GPoint(6, 7), lc->path.points[0]);
|
||||
cl_assert_equal_point(GPoint(8, 9), lc->path.points[1]);
|
||||
cl_assert_equal_point(GPoint(11, 12), lc->path.points[2]);
|
||||
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR(
|
||||
"ctx.arc(1, 2, 3, 4, 5);\n"
|
||||
"ctx.fill();\n"
|
||||
, "TypeError: fill() does not support arc()");
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics_path2d__fill_oom(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
prv_create_global_ctx();
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.moveTo(1, 2);\n"
|
||||
"ctx.lineTo(3, 4);\n"
|
||||
"ctx.lineTo(5, 6);\n"
|
||||
);
|
||||
|
||||
// OOM!
|
||||
fake_malloc_set_largest_free_block(0);
|
||||
|
||||
// Call implementation directly instead of executing a script, to avoid mallocs by the VM itself:
|
||||
extern jerry_value_t rocky_api_graphics_path2d_call_fill(void);
|
||||
const jerry_value_t error_value = rocky_api_graphics_path2d_call_fill();
|
||||
ASSERT_JS_ERROR(error_value, "RangeError: Out of memory: too many points to fill");
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics_path2d__arc(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
prv_create_global_ctx();
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.beginPath();\n"
|
||||
"ctx.moveTo(1, 2);\n"
|
||||
"ctx.arc(50, 40, 30, Math.PI, 0);\n"
|
||||
"ctx.arc(60, 80.1, 20.5, 0, Math.PI, false);\n"
|
||||
"ctx.stroke();\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(2, s_graphics_line_draw_precise_stroked.call_count);
|
||||
cl_assert_equal_i(2, s_graphics_draw_arc_precise.call_count);
|
||||
MockCallRecording *lc = &s_graphics_draw_arc_precise.last_call;
|
||||
cl_assert_equal_point_precise(PP(59.5, 79.625), lc->draw_arc.center);
|
||||
cl_assert_equal_i(20.5 * 8, lc->draw_arc.radius.raw_value);
|
||||
cl_assert_equal_i(TRIG_MAX_ANGLE * 1 / 4, lc->draw_arc.angle_start);
|
||||
cl_assert_equal_i(TRIG_MAX_ANGLE * 3 / 4, lc->draw_arc.angle_end);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics_path2d__anti_clockwise(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
prv_create_global_ctx();
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.beginPath();\n"
|
||||
"ctx.moveTo(80, 40);\n"
|
||||
"ctx.arc(60, 80, 20, 0, Math.PI, true);\n"
|
||||
"ctx.stroke();\n"
|
||||
);
|
||||
|
||||
cl_assert_equal_i(1, s_graphics_line_draw_precise_stroked.call_count);
|
||||
|
||||
cl_assert_equal_i(1, s_graphics_draw_arc_precise.call_count);
|
||||
MockCallRecording *lc = &s_graphics_draw_arc_precise.last_call;
|
||||
cl_assert_equal_point_precise(PP(59.5, 79.5), lc->draw_arc.center);
|
||||
cl_assert_equal_i(20 * 8, lc->draw_arc.radius.raw_value);
|
||||
cl_assert_equal_i(TRIG_MAX_ANGLE * 3 / 4, lc->draw_arc.angle_start);
|
||||
cl_assert_equal_i(TRIG_MAX_ANGLE * 5 / 4, lc->draw_arc.angle_end);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics_path2d__unsupported(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
prv_create_global_ctx();
|
||||
|
||||
EXECUTE_SCRIPT_EXPECT_UNDEFINED("ctx.arcTo");
|
||||
EXECUTE_SCRIPT_EXPECT_UNDEFINED("ctx.bezierCurveTo");
|
||||
EXECUTE_SCRIPT_EXPECT_UNDEFINED("ctx.quadraticCurveTo");
|
||||
}
|
||||
|
||||
extern size_t s_rocky_path_steps_num;
|
||||
extern RockyAPIPathStep *s_rocky_path_steps;
|
||||
|
||||
|
||||
void test_rocky_api_graphics_path2d__state_initialized_between_renders(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
s_rocky_path_steps_num = 2;
|
||||
EXECUTE_SCRIPT("_rocky.on('draw', function(e) {});");
|
||||
Layer *l = &app_window_stack_get_top_window()->layer;
|
||||
l->update_proc(l, NULL);
|
||||
|
||||
cl_assert_equal_i(0, s_rocky_path_steps_num);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics_path2d__rect(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
prv_create_global_ctx();
|
||||
|
||||
cl_assert_equal_i(0, s_rocky_path_steps_num);
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.moveTo(1, 2);\n"
|
||||
"ctx.rect(3, 4, 5, 6);\n"
|
||||
);
|
||||
cl_assert_equal_i(6, s_rocky_path_steps_num);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.rect(7, 8, 9, 10);\n"
|
||||
);
|
||||
cl_assert_equal_i(11, s_rocky_path_steps_num);
|
||||
cl_assert_equal_i(RockyAPIPathStepType_MoveTo, s_rocky_path_steps[0].type);
|
||||
cl_assert_equal_i(RockyAPIPathStepType_MoveTo, s_rocky_path_steps[1].type);
|
||||
cl_assert_equal_i(RockyAPIPathStepType_LineTo, s_rocky_path_steps[5].type);
|
||||
cl_assert_equal_i(RockyAPIPathStepType_MoveTo, s_rocky_path_steps[6].type);
|
||||
|
||||
cl_assert_equal_point_precise((GPointPrecise(20, 28)), s_rocky_path_steps[1].pt.xy);
|
||||
cl_assert_equal_point_precise((GPointPrecise(60, 76)), s_rocky_path_steps[3].pt.xy);
|
||||
cl_assert_equal_point_precise((GPointPrecise(20, 28)), s_rocky_path_steps[5].pt.xy);
|
||||
|
||||
// actual correctness of these values is test in test_rocky_api_graphics_rendering.c
|
||||
cl_assert_equal_vector_precise((GVectorPrecise(0, 8)), s_rocky_path_steps[1].pt.fill_delta);
|
||||
cl_assert_equal_vector_precise((GVectorPrecise(8, 0)), s_rocky_path_steps[3].pt.fill_delta);
|
||||
cl_assert_equal_vector_precise((GVectorPrecise(0, 8)), s_rocky_path_steps[5].pt.fill_delta);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics_path2d__close_path(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
prv_create_global_ctx();
|
||||
|
||||
cl_assert_equal_i(0, s_rocky_path_steps_num);
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.moveTo(1, 2);\n"
|
||||
"ctx.closePath();\n"
|
||||
);
|
||||
cl_assert_equal_i(1, s_rocky_path_steps_num);
|
||||
EXECUTE_SCRIPT(
|
||||
"ctx.lineTo(3, 4);\n"
|
||||
"ctx.closePath();\n"
|
||||
);
|
||||
cl_assert_equal_i(3, s_rocky_path_steps_num);
|
||||
cl_assert_equal_i(RockyAPIPathStepType_LineTo, s_rocky_path_steps[2].type);
|
||||
cl_assert_equal_point_precise(GPointPrecise(4, 12), (s_rocky_path_steps[0].pt.xy));
|
||||
cl_assert_equal_point_precise(GPointPrecise(4, 12), (s_rocky_path_steps[2].pt.xy));
|
||||
}
|
||||
|
||||
extern jerry_value_t rocky_api_graphics_path2d_try_allocate_steps(size_t increment_steps);
|
||||
extern size_t rocky_api_graphics_path2d_min_array_len(void);
|
||||
extern size_t rocky_api_graphics_path2d_array_len(void);
|
||||
|
||||
void test_rocky_api_graphics_path2d__initial_increment_larger_than_initial_size(void) {
|
||||
cl_assert_equal_i(rocky_api_graphics_path2d_array_len(), 0);
|
||||
const size_t min_size = rocky_api_graphics_path2d_min_array_len();
|
||||
const jerry_value_t rv = rocky_api_graphics_path2d_try_allocate_steps(min_size + 1);
|
||||
ASSERT_JS_ERROR(rv, NULL);
|
||||
jerry_release_value(rv);
|
||||
const size_t actual_size = rocky_api_graphics_path2d_array_len();
|
||||
cl_assert(actual_size >= min_size + 1);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics_path2d__array_realloc_oom(void) {
|
||||
fake_malloc_set_largest_free_block(0);
|
||||
const jerry_value_t rv = rocky_api_graphics_path2d_try_allocate_steps(1);
|
||||
ASSERT_JS_ERROR(rv, "RangeError: Out of memory: can't create more path steps");
|
||||
jerry_release_value(rv);
|
||||
}
|
312
tests/fw/javascript/test_rocky_api_graphics_rendering.c
Normal file
312
tests/fw/javascript/test_rocky_api_graphics_rendering.c
Normal file
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
#include "test_jerry_port_common.h"
|
||||
|
||||
#define DO_NOT_STUB_LEGACY2 1
|
||||
#include "test_rocky_common.h"
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/rockyjs/api/rocky_api.h"
|
||||
#include "applib/rockyjs/api/rocky_api_global.h"
|
||||
#include "applib/rockyjs/api/rocky_api_graphics.h"
|
||||
#include "applib/rockyjs/api/rocky_api_graphics_text.h"
|
||||
#include "applib/rockyjs/pbl_jerry_port.h"
|
||||
#include "util/trig.h"
|
||||
#include "applib/graphics/framebuffer.h"
|
||||
|
||||
#include "../graphics/util.h"
|
||||
|
||||
|
||||
// Standard
|
||||
#include "string.h"
|
||||
|
||||
// Fakes
|
||||
#include "fake_app_timer.h"
|
||||
#include "fake_time.h"
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_manager.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_sleep.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_sys_exit.h"
|
||||
|
||||
size_t heap_bytes_free(void) {
|
||||
return 123456;
|
||||
}
|
||||
|
||||
bool gbitmap_init_with_png_data(GBitmap *bitmap, const uint8_t *data, size_t data_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool gbitmap_png_data_is_png(const uint8_t *data, size_t data_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void layer_get_unobstructed_bounds(const Layer *layer, GRect *bounds_out) {
|
||||
*bounds_out = layer->bounds;
|
||||
}
|
||||
|
||||
void layer_mark_dirty(Layer *layer) {}
|
||||
|
||||
static Window s_app_window_stack_get_top_window;
|
||||
Window *app_window_stack_get_top_window() {
|
||||
return &s_app_window_stack_get_top_window;
|
||||
}
|
||||
|
||||
// no text rendering in this test
|
||||
void rocky_api_graphics_text_init(void) {}
|
||||
void rocky_api_graphics_text_deinit(void) {}
|
||||
void rocky_api_graphics_text_add_canvas_methods(jerry_value_t obj) {}
|
||||
void rocky_api_graphics_text_reset_state(void) {}
|
||||
|
||||
|
||||
//GBitmap s_bitmap;
|
||||
//uint8_t s_bitmap_data[DISPLAY_FRAMEBUFFER_BYTES];
|
||||
GContext s_context;
|
||||
FrameBuffer *s_framebuffer;
|
||||
GBitmap *s_pixels;
|
||||
|
||||
static void prv_init_gcontext(GSize size) {
|
||||
graphics_context_init(&s_context, s_framebuffer, GContextInitializationMode_App);
|
||||
framebuffer_clear(s_framebuffer);
|
||||
if (s_pixels) {
|
||||
gbitmap_destroy(s_pixels);
|
||||
}
|
||||
s_pixels = gbitmap_create_blank(size, GBITMAP_NATIVE_FORMAT);
|
||||
memset(s_pixels->addr, 0xff, size.h * s_pixels->row_size_bytes);
|
||||
s_context.dest_bitmap = *s_pixels;
|
||||
s_context.draw_state.clip_box = (GRect){.size = size};
|
||||
s_context.draw_state.drawing_box = s_context.draw_state.clip_box;
|
||||
s_app_state_get_graphics_context = &s_context;
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics_rendering__initialize(void) {
|
||||
fake_app_timer_init();
|
||||
rocky_runtime_context_init();
|
||||
jerry_init(JERRY_INIT_EMPTY);
|
||||
|
||||
s_framebuffer = malloc(sizeof(FrameBuffer));
|
||||
framebuffer_init(s_framebuffer, &(GSize) { DISP_COLS, DISP_ROWS });
|
||||
s_app_window_stack_get_top_window = (Window){};
|
||||
|
||||
prv_init_gcontext((GSize) { DISP_COLS, DISP_ROWS });
|
||||
s_app_event_loop_callback = NULL;
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics_rendering__cleanup(void) {
|
||||
fake_app_timer_deinit();
|
||||
|
||||
// some tests deinitialize the engine, avoid double de-init
|
||||
if (app_state_get_rocky_runtime_context() != NULL) {
|
||||
jerry_cleanup();
|
||||
rocky_runtime_context_deinit();
|
||||
}
|
||||
gbitmap_destroy(s_pixels);
|
||||
s_pixels = NULL;
|
||||
free(s_framebuffer);
|
||||
}
|
||||
|
||||
static const RockyGlobalAPI *s_graphics_api[] = {
|
||||
&GRAPHIC_APIS,
|
||||
NULL,
|
||||
};
|
||||
|
||||
jerry_value_t prv_create_canvas_context_2d_for_layer(Layer *layer);
|
||||
|
||||
static const jerry_value_t prv_global_init_and_set_ctx(void) {
|
||||
rocky_global_init(s_graphics_api);
|
||||
|
||||
// make this easily testable by putting it int JS context as global
|
||||
Layer l = {.bounds = GRect(0, 0, 144, 168)};
|
||||
const jerry_value_t ctx = prv_create_canvas_context_2d_for_layer(&l);
|
||||
jerry_set_object_field(jerry_get_global_object(), "ctx", ctx);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
void test_rocky_api_graphics_rendering__lines(void) {
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
// taken from http://fiddle.jshell.net/9298zub9/2/
|
||||
EXECUTE_SCRIPT(
|
||||
"var t1 = 10;\n"
|
||||
"var b1 = 20.5;\n"
|
||||
"var t2 = 30.5;\n"
|
||||
"var b2 = 40;\n"
|
||||
" \n"
|
||||
"for (var i = 1; i <= 5; i++) {\n"
|
||||
" ctx.beginPath();\n"
|
||||
" var x1 = 20 * i;\n"
|
||||
" var x2 = x1 + 10.5; \n"
|
||||
" ctx.moveTo(x1, t1);\n"
|
||||
" ctx.lineTo(x1, b1);\n"
|
||||
" ctx.moveTo(x2, t1);\n"
|
||||
" ctx.lineTo(x2, b1);\n"
|
||||
"\n"
|
||||
" ctx.moveTo(x1, t2);\n"
|
||||
" ctx.lineTo(x1, b2);\n"
|
||||
" ctx.moveTo(x2, t2);\n"
|
||||
" ctx.lineTo(x2, b2);\n"
|
||||
"\n"
|
||||
" ctx.lineWidth = i;\n"
|
||||
" ctx.stroke();\n"
|
||||
"}\n"
|
||||
"for (var i = 1; i <= 5; i++) {\n"
|
||||
" ctx.beginPath();\n"
|
||||
" var y1 = 40 + i * 20;\n"
|
||||
" var y2 = y1 + 10.5;\n"
|
||||
" ctx.moveTo(t1, y1);\n"
|
||||
" ctx.lineTo(b1, y1);\n"
|
||||
" ctx.moveTo(t1, y2);\n"
|
||||
" ctx.lineTo(b1, y2);\n"
|
||||
" \n"
|
||||
" ctx.moveTo(t2, y1);\n"
|
||||
" ctx.lineTo(b2, y1);\n"
|
||||
" ctx.moveTo(t2, y2);\n"
|
||||
" ctx.lineTo(b2, y2);\n"
|
||||
"\n"
|
||||
" ctx.lineWidth = i;\n"
|
||||
" ctx.stroke();\n"
|
||||
"}\n"
|
||||
"for (var i = 1; i <= 5; i++) {\n"
|
||||
" ctx.beginPath();\n"
|
||||
" var xx = 50;\n"
|
||||
" var yy = 50;\n"
|
||||
" var d = 15 * i;\n"
|
||||
" ctx.moveTo(xx, yy + d);\n"
|
||||
" ctx.lineTo(xx + d, yy);\n"
|
||||
"\n"
|
||||
" ctx.lineWidth = i;\n"
|
||||
" ctx.stroke();\n"
|
||||
"}"
|
||||
);
|
||||
|
||||
const bool eq_result =
|
||||
gbitmap_pbi_eq(&s_context.dest_bitmap, TEST_NAMED_PBI_FILE("rocky_rendering_lines"));
|
||||
cl_check(eq_result);
|
||||
}
|
||||
|
||||
void test_rocky_api_graphics_rendering__rect(void) {
|
||||
prv_init_gcontext(GSize(500, 150));
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
// taken from http://fiddle.jshell.net/a5gjzb7c/6/
|
||||
EXECUTE_SCRIPT(
|
||||
"function render(x, y, f) {\n"
|
||||
" f(x + 10, y + 10, 10, 10);\n"
|
||||
" f(x + 30.2, y + 10, 10, 10.2);\n"
|
||||
" f(x + 50.5, y + 10, 10, 10);\n"
|
||||
" f(x + 70.7, y + 10, 10.5, 10.8);\n"
|
||||
" f(x + 10, y + 30.5, 10, 10);\n"
|
||||
" f(x + 30.2, y + 30.5, 10, 10.2);\n"
|
||||
" f(x + 50.5, y + 30.5, 10, 10);\n"
|
||||
" f(x + 70.7, y + 30.5, 10.5, 10.8);\n"
|
||||
" \n"
|
||||
" f(x + 90, y + 10, 0, 0);\n"
|
||||
" f(x + 110, y + 10, 0.5, 0.5);\n"
|
||||
" f(x + 90, y + 30, -2, -2);\n"
|
||||
" f(x + 110, y + 30, -5.5, -6);\n"
|
||||
"}"
|
||||
"\n"
|
||||
"for (var i = 0; i <= 3; i++) {\n"
|
||||
" ctx.lineWidth = i;\n"
|
||||
" var x = 120 * i;\n"
|
||||
" render(x, 0, ctx[i == 0 ? 'fillRect' : 'strokeRect'].bind(ctx));\n"
|
||||
" render(x, 50, function(x,y,w,h) {\n"
|
||||
" ctx.beginPath();\n"
|
||||
" ctx.rect(x, y, w, h);\n"
|
||||
" ctx[i == 0? 'fill' : 'stroke'](); \n"
|
||||
" });\n"
|
||||
" render(x, 100, function r(x, y, w, h) {\n"
|
||||
" ctx.beginPath();\n"
|
||||
" ctx.moveTo(x, y);\n"
|
||||
" ctx.lineTo(x + w, y);\n"
|
||||
" ctx.lineTo(x + w, y + h);\n"
|
||||
" ctx.lineTo(x, y + h);\n"
|
||||
" ctx.lineTo(x, y);\n"
|
||||
" ctx[i == 0? 'fill' : 'stroke'](); \n"
|
||||
" });\n"
|
||||
"}"
|
||||
);
|
||||
|
||||
const bool eq_result =
|
||||
gbitmap_pbi_eq(&s_context.dest_bitmap, TEST_NAMED_PBI_FILE("rocky_rendering_rect"));
|
||||
cl_check(eq_result);
|
||||
}
|
||||
|
||||
|
||||
void test_rocky_api_graphics_rendering__arc(void) {
|
||||
prv_init_gcontext(GSize(500, 300));
|
||||
prv_global_init_and_set_ctx();
|
||||
|
||||
// http://fiddle.jshell.net/uopr1ez2/2/
|
||||
EXECUTE_SCRIPT(
|
||||
"var xx = 200;\n"
|
||||
"\n"
|
||||
"function f(x, y, r, a1, a2) {\n"
|
||||
" ctx.beginPath();\n"
|
||||
" ctx.arc(x, y, r, a1, a2, false);\n"
|
||||
" ctx.stroke();\n"
|
||||
"\n"
|
||||
" ctx.rockyFillRadial(x + xx, y, 0, r, a1, a2);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"function g(x, y, a1, a2) {\n"
|
||||
" f(x, y, 5, a1, a2);\n"
|
||||
" f(x, y, 15.5, a1, a2);\n"
|
||||
" f(x, y, 25.2, a1, a2);\n"
|
||||
" f(x, y, 34.8, a1, a2);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"function h(x, y, a1, a2) {\n"
|
||||
" for (var i = 0; i < 4; i++) {\n"
|
||||
" ctx.lineWidth = i + 1;\n"
|
||||
" g(x, y + 40 * i, a1, a2);\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"h(2, 2, 0, 0.5 * Math.PI);\n"
|
||||
"h(50.5, 2.5, 0, 0.5 * Math.PI);\n"
|
||||
"h(100.2, 2.2, 0, 0.5 * Math.PI);\n"
|
||||
"h(150.8, 2.8, 0, 0.5 * Math.PI);\n"
|
||||
"\n"
|
||||
"ctx.lineWidth = 1;\n"
|
||||
"f(20, 200, 10, 0, 2 * Math.PI);\n"
|
||||
"f(60.5, 200, 10, 0, 2 * Math.PI);\n"
|
||||
"f(100.5, 200.5, 10, 0, 2 * Math.PI);\n"
|
||||
"f(140, 200.5, 10, 0, 2 * Math.PI);\n"
|
||||
"\n"
|
||||
"f(20, 240, 11, 0, 2 * Math.PI);\n"
|
||||
"f(60.5, 240, 11, 0, 2 * Math.PI);\n"
|
||||
"f(100.5, 240.5, 11, 0, 2 * Math.PI);\n"
|
||||
"f(140, 240.5, 11, 0, 2 * Math.PI);\n"
|
||||
"\n"
|
||||
"f(20, 280, 11, 0, -0.5 * Math.PI);"
|
||||
);
|
||||
|
||||
const bool eq_result =
|
||||
gbitmap_pbi_eq(&s_context.dest_bitmap, TEST_NAMED_PBI_FILE("rocky_rendering_arc"));
|
||||
cl_check(eq_result);
|
||||
}
|
265
tests/fw/javascript/test_rocky_api_memory.c
Normal file
265
tests/fw/javascript/test_rocky_api_memory.c
Normal file
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
#include "test_jerry_port_common.h"
|
||||
#include "test_rocky_common.h"
|
||||
|
||||
#include "applib/rockyjs/api/rocky_api_global.h"
|
||||
#include "applib/rockyjs/api/rocky_api_memory.h"
|
||||
#include "applib/rockyjs/pbl_jerry_port.h"
|
||||
|
||||
#include "syscall/syscall.h"
|
||||
|
||||
#include <jmem/jmem-heap.h>
|
||||
|
||||
// Standard
|
||||
#include <string.h>
|
||||
|
||||
// Fakes
|
||||
#include "fake_app_timer.h"
|
||||
#include "fake_logging.h"
|
||||
#include "fake_pbl_malloc.h"
|
||||
#include "fake_time.h"
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_manager.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_sleep.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_syscalls.h"
|
||||
|
||||
size_t heap_bytes_free(void) {
|
||||
return 123456;
|
||||
}
|
||||
|
||||
static int s_sys_analytics_inc_call_count;
|
||||
void sys_analytics_inc(AnalyticsMetric metric, AnalyticsClient client) {
|
||||
cl_assert_equal_i(metric, ANALYTICS_APP_METRIC_MEM_ROCKY_RECURSIVE_MEMORYPRESSURE_EVENT_COUNT);
|
||||
++s_sys_analytics_inc_call_count;
|
||||
}
|
||||
|
||||
static const RockyGlobalAPI *s_api[] = {
|
||||
&MEMORY_APIS,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static bool s_skip_pbl_malloc_check;
|
||||
|
||||
#define assert_oom_app_fault() \
|
||||
cl_assert_equal_i(s_app_heap_analytics_log_rocky_heap_oom_fault_call_count, 1)
|
||||
|
||||
#define assert_no_oom_app_fault() \
|
||||
cl_assert_equal_i(s_app_heap_analytics_log_rocky_heap_oom_fault_call_count, 0)
|
||||
|
||||
void test_rocky_api_memory__initialize(void) {
|
||||
s_sys_analytics_inc_call_count = 0;
|
||||
s_app_heap_analytics_log_rocky_heap_oom_fault_call_count = 0;
|
||||
|
||||
fake_pbl_malloc_clear_tracking();
|
||||
s_skip_pbl_malloc_check = false;
|
||||
|
||||
s_log_internal__expected = NULL;
|
||||
|
||||
rocky_runtime_context_init();
|
||||
jerry_init(JERRY_INIT_EMPTY);
|
||||
rocky_global_init(s_api);
|
||||
}
|
||||
|
||||
void test_rocky_api_memory__cleanup(void) {
|
||||
rocky_global_deinit();
|
||||
jerry_cleanup();
|
||||
rocky_runtime_context_deinit();
|
||||
|
||||
if (!s_skip_pbl_malloc_check) {
|
||||
fake_pbl_malloc_check_net_allocs();
|
||||
}
|
||||
}
|
||||
|
||||
void test_rocky_api_memory__event(void) {
|
||||
cl_assert_equal_b(false, rocky_global_has_event_handlers("memorypressure"));
|
||||
|
||||
jmem_heap_stats_t before_stats = {};
|
||||
jmem_heap_get_stats(&before_stats);
|
||||
|
||||
EXECUTE_SCRIPT("_rocky.on('memorypressure', function(){});");
|
||||
|
||||
// After registering a handler for 'memorypressure', expect a drop of more than N bytes of heap
|
||||
// because of the reservation of headroom space:
|
||||
jmem_heap_stats_t after_stats = {};
|
||||
jmem_heap_get_stats(&after_stats);
|
||||
cl_assert(after_stats.allocated_bytes - before_stats.allocated_bytes >=
|
||||
ROCKY_API_MEMORY_HEADROOM_DESIRED_SIZE_BYTES);
|
||||
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("memorypressure"));
|
||||
}
|
||||
|
||||
void test_rocky_api_memory__oom_app_fault_if_handler_allocates_more_than_headroom(void) {
|
||||
s_skip_pbl_malloc_check = true;
|
||||
cl_assert_passert(EXECUTE_SCRIPT(
|
||||
"var data = [];\n"
|
||||
"_rocky.on('memorypressure', function(){\n"
|
||||
" var handlerData = [];\n"
|
||||
" for (var i = 0; i < 100000; i++) {handlerData.push(i);}\n"
|
||||
"});\n"
|
||||
"for (var i = 0; i < 100000; i++) {data.push(i);}\n"
|
||||
));
|
||||
assert_oom_app_fault();
|
||||
cl_assert_equal_i(s_sys_analytics_inc_call_count, 1);
|
||||
}
|
||||
|
||||
void test_rocky_api_memory__no_oom_app_fault_if_handler_frees_up_enough_memory_empty_array(void) {
|
||||
// Note to the reader: the lifecycle of `data` is not what you might think it is on first sight:
|
||||
// When `data = [];` executes, the original `data` will still be retained, because the original
|
||||
// execution context is still on the stack. Only after the the 'memorypressure' handler returns
|
||||
// and that for(){} block finishes, is the original `data` released!
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"var data = [];\n"
|
||||
"var level = undefined;"
|
||||
"_rocky.on('memorypressure', function(e){\n"
|
||||
" level = e.level;\n"
|
||||
" data = [];\n"
|
||||
"});\n"
|
||||
"for (var i = 0; i < 100000; i++) {data.push(i);}\n"
|
||||
);
|
||||
assert_no_oom_app_fault();
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("level", "high");
|
||||
cl_assert_equal_i(s_sys_analytics_inc_call_count, 0);
|
||||
}
|
||||
|
||||
void test_rocky_api_memory__no_oom_app_fault_if_handler_frees_up_enough_memory_empty_object(void) {
|
||||
EXECUTE_SCRIPT(
|
||||
"var data = {};\n"
|
||||
"_rocky.on('memorypressure', function(e){\n"
|
||||
" data = {};\n"
|
||||
"});\n"
|
||||
"for (var i = 0; i < 100000; i++) {data[i] = i;}\n"
|
||||
);
|
||||
assert_no_oom_app_fault();
|
||||
cl_assert_equal_i(s_sys_analytics_inc_call_count, 0);
|
||||
}
|
||||
|
||||
void test_rocky_api_memory__no_oom_app_fault_if_handler_frees_up_enough_memory_put_props_for(void) {
|
||||
// This example uses a lot of properties on an Object to store things.
|
||||
// When running out of memory, these are dropped to free up memory, using the `delete` operator.
|
||||
EXECUTE_SCRIPT(
|
||||
"var first = 0;\n"
|
||||
"var i = 0;\n"
|
||||
"var obj = {};\n"
|
||||
"_rocky.on('memorypressure', function(e){\n"
|
||||
" for (var j = first; j < i; j++) {\n"
|
||||
" delete obj[j];\n"
|
||||
" }\n"
|
||||
" first = i;\n"
|
||||
"});\n"
|
||||
"for (i = first; i < 100000; i++) {\n"
|
||||
" obj[i] = i;"
|
||||
"}\n"
|
||||
);
|
||||
assert_no_oom_app_fault();
|
||||
cl_assert_equal_i(s_sys_analytics_inc_call_count, 0);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// This doesn't work at the moment, because the `in` operator allocates a ton of memory... :( for
|
||||
// the same reason as why Array.pop() has a footprint that's proportional to the number of elements.
|
||||
void test_rocky_api_memory
|
||||
__no_oom_app_fault_if_handler_frees_up_enough_mem_put_props_for_in(void) {
|
||||
EXECUTE_SCRIPT(
|
||||
"var obj = {};\n"
|
||||
"_rocky.on('memorypressure', function(e){\n"
|
||||
" for (var p in obj) {\n"
|
||||
" delete obj[p];\n"
|
||||
" }\n"
|
||||
"});\n"
|
||||
"for (var i = 0; i < 100000; i++) {\n"
|
||||
" obj['' + i] = i;"
|
||||
"}\n"
|
||||
);
|
||||
assert_no_oom_app_fault();
|
||||
cl_assert_equal_i(s_sys_analytics_inc_call_count, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// This doesn't work because the putting the `length` property of an array end up calling
|
||||
// ecma_op_object_get_property_names, which is has a memory footprint proportional to the number of
|
||||
// elements/properties..
|
||||
void test_rocky_api_memory
|
||||
__no_oom_app_fault_if_handler_frees_up_enough_memory_put_length(void) {
|
||||
EXECUTE_SCRIPT(
|
||||
"var cache = [];\n"
|
||||
"_rocky.on('memorypressure', function(event) {\n"
|
||||
" while (cache.length > 0) {\n"
|
||||
" delete cache[cache.length - 1];\n"
|
||||
" --cache.length;\n"
|
||||
" }\n"
|
||||
"})\n;"
|
||||
"for (var i = 0; i < 100000; i++) {\n"
|
||||
" cache.push(i);\n"
|
||||
"}\n"
|
||||
);
|
||||
assert_no_oom_app_fault();
|
||||
cl_assert_equal_i(s_sys_analytics_inc_call_count, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// This doesn't work at the moment, because https://github.com/Samsung/jerryscript/issues/1370
|
||||
void test_rocky_api_memory
|
||||
__no_oom_app_fault_if_handler_frees_up_enough_memory_simple(void) {
|
||||
EXECUTE_SCRIPT(
|
||||
"var cache = [];\n"
|
||||
"_rocky.on('memorypressure', function(event) {\n"
|
||||
" while (cache.length > 0) {\n"
|
||||
" cache.pop();\n"
|
||||
" }\n"
|
||||
"})\n;"
|
||||
"for (var i = 0; i < 100000; i++) {\n"
|
||||
" cache.push(i);\n"
|
||||
"}\n"
|
||||
);
|
||||
assert_no_oom_app_fault();
|
||||
cl_assert_equal_i(s_sys_analytics_inc_call_count, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
void test_rocky_api_memory__oom_app_fault_if_handler_does_not_free_up_enough_memory(void) {
|
||||
s_skip_pbl_malloc_check = true;
|
||||
|
||||
s_log_internal__expected_regex = (const char *[]){
|
||||
"Memory pressure level: high",
|
||||
"heap size: [0-9]+, alloc'd: [0-9]+, waste: [0-9]+, largest free block: [0-9]+,",
|
||||
"used blocks: [0-9]+, free blocks: [0-9]+",
|
||||
"Fatal Error: 10",
|
||||
NULL };
|
||||
|
||||
cl_assert_passert(EXECUTE_SCRIPT(
|
||||
"var data = [];\n"
|
||||
"var shouldContinue = true;\n"
|
||||
"_rocky.on('memorypressure', function(){\n"
|
||||
" shouldContinue = false;\n"
|
||||
"});\n"
|
||||
"for (var i = 0; shouldContinue && i < 100000; i++) {data.push(i);}\n"
|
||||
));
|
||||
assert_oom_app_fault();
|
||||
cl_assert_equal_i(s_sys_analytics_inc_call_count, 0);
|
||||
cl_assert(*s_log_internal__expected_regex == NULL);
|
||||
}
|
113
tests/fw/javascript/test_rocky_api_preferences.c
Normal file
113
tests/fw/javascript/test_rocky_api_preferences.c
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
#include "test_jerry_port_common.h"
|
||||
#include "test_rocky_common.h"
|
||||
|
||||
#include "applib/rockyjs/api/rocky_api_global.h"
|
||||
#include "applib/rockyjs/api/rocky_api_preferences.h"
|
||||
#include "applib/rockyjs/pbl_jerry_port.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <applib/preferred_content_size.h>
|
||||
|
||||
// Fakes
|
||||
#include "fake_app_timer.h"
|
||||
#include "fake_time.h"
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_sys_exit.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Fakes / Stubs
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static PreferredContentSize s_preferred_content_size;
|
||||
PreferredContentSize preferred_content_size(void) {
|
||||
return s_preferred_content_size;
|
||||
}
|
||||
|
||||
static const RockyGlobalAPI *s_preferences_api[] = {
|
||||
&PREFERENCES_APIS,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void test_rocky_api_preferences__initialize(void) {
|
||||
rocky_runtime_context_init();
|
||||
jerry_init(JERRY_INIT_EMPTY);
|
||||
s_preferred_content_size = PreferredContentSizeMedium;
|
||||
}
|
||||
|
||||
void test_rocky_api_preferences__cleanup(void) {
|
||||
if (app_state_get_rocky_runtime_context() != NULL) {
|
||||
jerry_cleanup();
|
||||
rocky_runtime_context_deinit();
|
||||
}
|
||||
}
|
||||
|
||||
void test_rocky_api_preferences__unknown(void) {
|
||||
s_preferred_content_size = (PreferredContentSize) -1;
|
||||
rocky_global_init(s_preferences_api);
|
||||
|
||||
EXECUTE_SCRIPT("var size = _rocky.userPreferences.contentSize");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("size", "medium");
|
||||
}
|
||||
|
||||
void test_rocky_api_preferences__always_valid(void) {
|
||||
s_preferred_content_size = NumPreferredContentSizes;
|
||||
rocky_global_init(s_preferences_api);
|
||||
|
||||
EXECUTE_SCRIPT("var size = _rocky.userPreferences.contentSize");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("size", "medium");
|
||||
}
|
||||
|
||||
void test_rocky_api_preferences__small(void) {
|
||||
s_preferred_content_size = PreferredContentSizeSmall;
|
||||
rocky_global_init(s_preferences_api);
|
||||
|
||||
EXECUTE_SCRIPT("var size = _rocky.userPreferences.contentSize");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("size", "small");
|
||||
}
|
||||
|
||||
void test_rocky_api_preferences__medium(void) {
|
||||
s_preferred_content_size = PreferredContentSizeMedium;
|
||||
rocky_global_init(s_preferences_api);
|
||||
|
||||
EXECUTE_SCRIPT("var size = _rocky.userPreferences.contentSize");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("size", "medium");
|
||||
}
|
||||
|
||||
void test_rocky_api_preferences__large(void) {
|
||||
s_preferred_content_size = PreferredContentSizeLarge;
|
||||
rocky_global_init(s_preferences_api);
|
||||
|
||||
EXECUTE_SCRIPT("var size = _rocky.userPreferences.contentSize");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("size", "large");
|
||||
}
|
||||
|
||||
void test_rocky_api_preferences__extra_large(void) {
|
||||
s_preferred_content_size = PreferredContentSizeExtraLarge;
|
||||
rocky_global_init(s_preferences_api);
|
||||
|
||||
EXECUTE_SCRIPT("var size = _rocky.userPreferences.contentSize");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("size", "x-large");
|
||||
}
|
280
tests/fw/javascript/test_rocky_api_tickservice.c
Normal file
280
tests/fw/javascript/test_rocky_api_tickservice.c
Normal file
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
#include "test_jerry_port_common.h"
|
||||
#include "test_rocky_common.h"
|
||||
|
||||
#include "applib/rockyjs/api/rocky_api_global.h"
|
||||
#include "applib/rockyjs/api/rocky_api_tickservice.h"
|
||||
#include "applib/rockyjs/pbl_jerry_port.h"
|
||||
|
||||
// Standard
|
||||
#include "string.h"
|
||||
|
||||
// Fakes
|
||||
#include "fake_app_timer.h"
|
||||
#include "fake_logging.h"
|
||||
#if EMSCRIPTEN
|
||||
#include "fake_time_timeshift_js.h"
|
||||
#else
|
||||
#include "fake_time.h"
|
||||
#endif
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_manager.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_sleep.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_sys_exit.h"
|
||||
|
||||
size_t heap_bytes_free(void) {
|
||||
return 123456;
|
||||
}
|
||||
|
||||
void tick_timer_service_handle_time_change(void) {}
|
||||
|
||||
MockCallRecordings s_tick_timer_service_subscribe;
|
||||
void tick_timer_service_subscribe(TimeUnits tick_units, TickHandler handler) {
|
||||
record_mock_call(s_tick_timer_service_subscribe) {
|
||||
.tick_units = tick_units,
|
||||
};
|
||||
}
|
||||
|
||||
void test_rocky_api_tickservice__initialize(void) {
|
||||
fake_app_timer_init();
|
||||
rocky_runtime_context_init();
|
||||
jerry_init(JERRY_INIT_EMPTY);
|
||||
s_tick_timer_service_subscribe = (MockCallRecordings){0};
|
||||
s_log_internal__expected = NULL;
|
||||
}
|
||||
|
||||
void test_rocky_api_tickservice__cleanup(void) {
|
||||
jerry_cleanup();
|
||||
rocky_runtime_context_deinit();
|
||||
}
|
||||
|
||||
static const RockyGlobalAPI *s_api[] = {
|
||||
&TICKSERVICE_APIS,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void test_rocky_api_tickservice__provides_events(void) {
|
||||
rocky_global_init(s_api);
|
||||
|
||||
cl_assert_equal_i(0, s_tick_timer_service_subscribe.call_count);
|
||||
cl_assert_equal_b(false, rocky_global_has_event_handlers("secondchange"));
|
||||
cl_assert_equal_b(false, rocky_global_has_event_handlers("minutechange"));
|
||||
cl_assert_equal_b(false, rocky_global_has_event_handlers("hourchange"));
|
||||
cl_assert_equal_b(false, rocky_global_has_event_handlers("daychange"));
|
||||
|
||||
EXECUTE_SCRIPT("_rocky.on('daychange', function() {});");
|
||||
cl_assert_equal_b(false, rocky_global_has_event_handlers("secondchange"));
|
||||
cl_assert_equal_b(false, rocky_global_has_event_handlers("minutechange"));
|
||||
cl_assert_equal_b(false, rocky_global_has_event_handlers("hourchange"));
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("daychange"));
|
||||
cl_assert_equal_i(1, s_tick_timer_service_subscribe.call_count);
|
||||
cl_assert_equal_i(DAY_UNIT | MONTH_UNIT | YEAR_UNIT,
|
||||
s_tick_timer_service_subscribe.last_call.tick_units);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"var hourHandler = function() {};\n"
|
||||
"_rocky.on('hourchange', hourHandler);\n"
|
||||
);
|
||||
cl_assert_equal_b(false, rocky_global_has_event_handlers("secondchange"));
|
||||
cl_assert_equal_b(false, rocky_global_has_event_handlers("minutechange"));
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("hourchange"));
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("daychange"));
|
||||
cl_assert_equal_i(2, s_tick_timer_service_subscribe.call_count);
|
||||
cl_assert_equal_i(HOUR_UNIT | DAY_UNIT | MONTH_UNIT | YEAR_UNIT,
|
||||
s_tick_timer_service_subscribe.last_call.tick_units);
|
||||
|
||||
EXECUTE_SCRIPT("_rocky.on('minutechange', function() {});");
|
||||
cl_assert_equal_b(false, rocky_global_has_event_handlers("secondchange"));
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("minutechange"));
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("hourchange"));
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("daychange"));
|
||||
cl_assert_equal_i(3, s_tick_timer_service_subscribe.call_count);
|
||||
cl_assert_equal_i(MINUTE_UNIT | HOUR_UNIT | DAY_UNIT | MONTH_UNIT | YEAR_UNIT,
|
||||
s_tick_timer_service_subscribe.last_call.tick_units);
|
||||
|
||||
// register for minute again
|
||||
EXECUTE_SCRIPT("_rocky.on('minutechange', function() {});");
|
||||
cl_assert_equal_b(false, rocky_global_has_event_handlers("secondchange"));
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("minutechange"));
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("hourchange"));
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("daychange"));
|
||||
cl_assert_equal_i(4, s_tick_timer_service_subscribe.call_count);
|
||||
cl_assert_equal_i(MINUTE_UNIT | HOUR_UNIT | DAY_UNIT | MONTH_UNIT | YEAR_UNIT,
|
||||
s_tick_timer_service_subscribe.last_call.tick_units);
|
||||
|
||||
|
||||
EXECUTE_SCRIPT("_rocky.on('secondchange', function() {});");
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("secondchange"));
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("minutechange"));
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("hourchange"));
|
||||
cl_assert_equal_b(true, rocky_global_has_event_handlers("daychange"));
|
||||
cl_assert_equal_i(5, s_tick_timer_service_subscribe.call_count);
|
||||
cl_assert_equal_i(SECOND_UNIT | MINUTE_UNIT | HOUR_UNIT | DAY_UNIT | MONTH_UNIT | YEAR_UNIT,
|
||||
s_tick_timer_service_subscribe.last_call.tick_units);
|
||||
}
|
||||
|
||||
|
||||
void prv_tick_handler(struct tm *tick_time, TimeUnits units_changed);
|
||||
|
||||
void test_rocky_api_tickservice__calls_handlers(void) {
|
||||
rocky_global_init(s_api);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"var s = 0;\n"
|
||||
"var m = 0;\n"
|
||||
"var h = 0;\n"
|
||||
"var d = 0;\n"
|
||||
"_rocky.on('secondchange', function(e) {s++;});"
|
||||
"_rocky.on('minutechange', function(e) {m++;});"
|
||||
"_rocky.on('hourchange', function(e) {h++;});"
|
||||
"_rocky.on('daychange', function(e) {d++;});"
|
||||
);
|
||||
|
||||
// subscribing already triggers a call
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("s", 1);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("m", 1);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("h", 1);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("d", 1);
|
||||
|
||||
// all handlers will be called as year change means minute change
|
||||
prv_tick_handler(NULL, YEAR_UNIT);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("s", 2);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("m", 2);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("h", 2);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("d", 2);
|
||||
|
||||
// same here, each time a day changes, a second changes, too
|
||||
prv_tick_handler(NULL, MINUTE_UNIT | DAY_UNIT);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("s", 3);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("m", 3);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("h", 3);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("d", 3);
|
||||
|
||||
prv_tick_handler(NULL, HOUR_UNIT);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("s", 4);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("m", 4);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("h", 4);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("d", 3);
|
||||
|
||||
prv_tick_handler(NULL, MINUTE_UNIT);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("s", 5);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("m", 5);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("h", 4);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("d", 3);
|
||||
|
||||
prv_tick_handler(NULL, SECOND_UNIT);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("s", 6);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("m", 5);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("h", 4);
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("d", 3);
|
||||
}
|
||||
|
||||
void test_rocky_api_tickservice__event_types(void) {
|
||||
rocky_global_init(s_api);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"var s = null;\n"
|
||||
"var m = null;\n"
|
||||
"var h = null;\n"
|
||||
"var d = null;\n"
|
||||
"_rocky.on('secondchange', function(e) {s = e.type;});"
|
||||
"_rocky.on('minutechange', function(e) {m = e.type;});"
|
||||
"_rocky.on('hourchange', function(e) {h = e.type;});"
|
||||
"_rocky.on('daychange', function(e) {d = e.type;});"
|
||||
);
|
||||
|
||||
// subscribing already triggers a call
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("s", "secondchange");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("m", "minutechange");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("h", "hourchange");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("d", "daychange");
|
||||
}
|
||||
|
||||
void test_rocky_api_tickservice__error_in_handler_on_register(void) {
|
||||
rocky_global_init(s_api);
|
||||
|
||||
s_log_internal__expected = (const char *[]){
|
||||
"Unhandled exception",
|
||||
" secondchange",
|
||||
NULL
|
||||
};
|
||||
EXECUTE_SCRIPT(
|
||||
"_rocky.on('secondchange', function(e) { throw e.type; });"
|
||||
);
|
||||
cl_assert(*s_log_internal__expected == NULL);
|
||||
}
|
||||
|
||||
void test_rocky_api_tickservice__provides_event_date(void) {
|
||||
rocky_global_init(s_api);
|
||||
|
||||
s_log_internal__expected = (const char *[]){ NULL };
|
||||
|
||||
const time_t cur_time = 1458250851; // Thu Mar 17 21:40:51 2016 UTC
|
||||
// Thu Mar 17 14:40:51 2016 PDT
|
||||
const uint16_t cur_millis = 123;
|
||||
fake_time_init(cur_time, cur_millis);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"var s = null;\n"
|
||||
"var m = null;\n"
|
||||
"var h = null;\n"
|
||||
"var d = null;\n"
|
||||
"_rocky.on('secondchange', function(e) { s = e.date.getSeconds(); });\n"
|
||||
"_rocky.on('minutechange', function(e) { m = e.date.getMinutes(); });\n"
|
||||
"_rocky.on('hourchange', function(e) { h = e.date.getHours(); });\n"
|
||||
"_rocky.on('daychange', function(e) { d = e.date.getDate(); });\n"
|
||||
);
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_D("s", 51.0);
|
||||
ASSERT_JS_GLOBAL_EQUALS_D("m", 40.0);
|
||||
ASSERT_JS_GLOBAL_EQUALS_D("h", 21.0);
|
||||
ASSERT_JS_GLOBAL_EQUALS_D("d", 17.0);
|
||||
|
||||
EXECUTE_SCRIPT(
|
||||
"s = null;\n"
|
||||
"m = null;\n"
|
||||
"h = null;\n"
|
||||
"d = null;\n"
|
||||
);
|
||||
|
||||
struct tm tm = {
|
||||
.tm_sec = 1,
|
||||
.tm_min = 2,
|
||||
.tm_hour = 3,
|
||||
.tm_mday = 4,
|
||||
.tm_mon = 5,
|
||||
.tm_year = 116, // 2016
|
||||
};
|
||||
|
||||
prv_tick_handler(&tm, SECOND_UNIT | MINUTE_UNIT | HOUR_UNIT | DAY_UNIT);
|
||||
ASSERT_JS_GLOBAL_EQUALS_D("s", 1.0);
|
||||
ASSERT_JS_GLOBAL_EQUALS_D("m", 2.0);
|
||||
ASSERT_JS_GLOBAL_EQUALS_D("h", 3.0);
|
||||
ASSERT_JS_GLOBAL_EQUALS_D("d", 4.0);
|
||||
|
||||
cl_assert(*s_log_internal__expected == NULL);
|
||||
}
|
115
tests/fw/javascript/test_rocky_api_timers.c
Normal file
115
tests/fw/javascript/test_rocky_api_timers.c
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
#include "test_jerry_port_common.h"
|
||||
#include "test_rocky_common.h"
|
||||
|
||||
#include "applib/rockyjs/api/rocky_api_timers.h"
|
||||
#include "applib/rockyjs/pbl_jerry_port.h"
|
||||
|
||||
// Fakes
|
||||
#include "fake_app_timer.h"
|
||||
#include "fake_pbl_malloc.h"
|
||||
#include "fake_time.h"
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_manager.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_resources.h"
|
||||
#include "stubs_sleep.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_sys_exit.h"
|
||||
|
||||
|
||||
void test_rocky_api_timers__initialize(void) {
|
||||
fake_pbl_malloc_clear_tracking();
|
||||
fake_app_timer_init();
|
||||
rocky_runtime_context_init();
|
||||
jerry_init(JERRY_INIT_EMPTY);
|
||||
TIMER_APIS.init();
|
||||
}
|
||||
|
||||
void test_rocky_api_timers__cleanup(void) {
|
||||
jerry_cleanup();
|
||||
rocky_runtime_context_deinit();
|
||||
fake_app_timer_deinit();
|
||||
fake_pbl_malloc_check_net_allocs();
|
||||
}
|
||||
|
||||
|
||||
void test_rocky_api_timers__setInterval(void) {
|
||||
char *script =
|
||||
"var num_times = 0;"
|
||||
"var extra_arg = 0;"
|
||||
"var timer = setInterval(function(extra) {"
|
||||
"num_times++;"
|
||||
"extra_arg = extra;"
|
||||
"}, 1000, 5);";
|
||||
|
||||
EXECUTE_SCRIPT(script);
|
||||
|
||||
AppTimer *timer = (AppTimer *)(uintptr_t)prv_js_global_get_double("timer");
|
||||
|
||||
for (double d = 0.0; d < 5.0; d += 1.0) {
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("num_times", d);
|
||||
cl_assert(fake_app_timer_is_scheduled(timer));
|
||||
cl_assert(app_timer_trigger(timer));
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("extra_arg", 5.0);
|
||||
}
|
||||
|
||||
script = "clearInterval(timer);";
|
||||
EXECUTE_SCRIPT(script);
|
||||
cl_assert(fake_app_timer_is_scheduled(timer) == false);
|
||||
}
|
||||
|
||||
void test_rocky_api_timers__setTimeout(void) {
|
||||
char *script =
|
||||
"var num_times = 0;"
|
||||
"var f = function(extra) {"
|
||||
" num_times++;"
|
||||
"};"
|
||||
"var timer = setTimeout('f()', '1000');";
|
||||
|
||||
EXECUTE_SCRIPT(script);
|
||||
|
||||
AppTimer *timer = (AppTimer *)(uintptr_t)prv_js_global_get_double("timer");
|
||||
cl_assert_equal_i(fake_app_timer_get_timeout(timer), 1000);
|
||||
cl_assert(fake_app_timer_is_scheduled(timer));
|
||||
cl_assert(app_timer_trigger(timer));
|
||||
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("num_times", 1.0);
|
||||
|
||||
// Verified timer will not trigger again
|
||||
cl_assert(fake_app_timer_is_scheduled(timer) == false);
|
||||
}
|
||||
|
||||
void test_rocky_api_timers__bogus_clearInterval(void) {
|
||||
EXECUTE_SCRIPT_EXPECT_UNDEFINED("clearInterval(0)");
|
||||
EXECUTE_SCRIPT_EXPECT_UNDEFINED("clearInterval(1234)");
|
||||
EXECUTE_SCRIPT_EXPECT_UNDEFINED("clearInterval(-1234)");
|
||||
EXECUTE_SCRIPT_EXPECT_UNDEFINED("clearInterval(undefined)");
|
||||
}
|
||||
|
||||
void test_rocky_api_timers__bogus_clearTimeout(void) {
|
||||
EXECUTE_SCRIPT_EXPECT_UNDEFINED("clearTimeout(0)");
|
||||
EXECUTE_SCRIPT_EXPECT_UNDEFINED("clearTimeout(1234)");
|
||||
EXECUTE_SCRIPT_EXPECT_UNDEFINED("clearTimeout(-1234)");
|
||||
EXECUTE_SCRIPT_EXPECT_UNDEFINED("clearTimeout(undefined)");
|
||||
}
|
330
tests/fw/javascript/test_rocky_api_util.c
Normal file
330
tests/fw/javascript/test_rocky_api_util.c
Normal file
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "test_jerry_port_common.h"
|
||||
#include "test_rocky_common.h"
|
||||
#include "applib/rockyjs/pbl_jerry_port.h"
|
||||
#include "vendor/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h"
|
||||
|
||||
#include "applib/rockyjs/api/rocky_api_global.h"
|
||||
|
||||
#include <clar.h>
|
||||
#include <math.h>
|
||||
|
||||
// Fakes
|
||||
#include "fake_logging.h"
|
||||
#include "fake_pbl_malloc.h"
|
||||
#if EMSCRIPTEN
|
||||
#include "fake_time_timeshift_js.h"
|
||||
#else
|
||||
#include "fake_time.h"
|
||||
#endif
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_manager.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_sys_exit.h"
|
||||
|
||||
|
||||
#define FUNC_NAME "f"
|
||||
#define ERROR_STRING "Oops!"
|
||||
|
||||
T_STATIC void prv_log_uncaught_error(const jerry_value_t result);
|
||||
|
||||
static int s_test_func_imp_call_count;
|
||||
static int s_method_func_imp_call_count;
|
||||
|
||||
void tick_timer_service_handle_time_change(void) {}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Initialization & Setup
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void test_rocky_api_util__initialize(void) {
|
||||
s_test_func_imp_call_count = 0;
|
||||
s_method_func_imp_call_count = 0;
|
||||
rocky_runtime_context_init();
|
||||
jerry_init(JERRY_INIT_EMPTY);
|
||||
s_log_internal__expected = NULL;
|
||||
}
|
||||
|
||||
void test_rocky_api_util__cleanup(void) {
|
||||
jerry_cleanup();
|
||||
rocky_runtime_context_deinit();
|
||||
fake_pbl_malloc_check_net_allocs();
|
||||
s_log_internal__expected = NULL;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers for Tests
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void prv_do_call_user_function(const char *script) {
|
||||
const jerry_value_t rv = jerry_eval((jerry_char_t *)script, strlen(script),
|
||||
false /* is_strict */);
|
||||
cl_assert_equal_b(jerry_value_has_error_flag(rv), false);
|
||||
jerry_release_value(rv);
|
||||
|
||||
const jerry_value_t func = JS_GLOBAL_GET_VALUE(FUNC_NAME);
|
||||
rocky_util_call_user_function_and_log_uncaught_error(func, jerry_create_undefined(), NULL, 0);
|
||||
jerry_release_value(func);
|
||||
}
|
||||
|
||||
static void prv_do_eval(const char *eval_str) {
|
||||
rocky_util_eval_and_log_uncaught_error((const jerry_char_t *)eval_str, strlen(eval_str));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Tests
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JERRY_FUNCTION(test_func_imp) {
|
||||
++s_test_func_imp_call_count;
|
||||
return jerry_create_undefined();
|
||||
}
|
||||
|
||||
JERRY_FUNCTION(method_func_imp) {
|
||||
++s_method_func_imp_call_count;
|
||||
return jerry_create_undefined();
|
||||
}
|
||||
|
||||
void test_rocky_api_util__rocky_add_constructor(void) {
|
||||
static const RockyGlobalAPI *s_api[] = {
|
||||
NULL,
|
||||
};
|
||||
rocky_global_init(s_api);
|
||||
|
||||
JS_VAR prototype = rocky_add_constructor("test", test_func_imp);
|
||||
cl_assert_equal_b(jerry_value_is_object(prototype), true);
|
||||
EXECUTE_SCRIPT("_rocky.test();");
|
||||
cl_assert_equal_i(1, s_test_func_imp_call_count);
|
||||
|
||||
rocky_add_function(prototype, "method", method_func_imp);
|
||||
EXECUTE_SCRIPT("var y = new _rocky.test(); y.method();");
|
||||
cl_assert_equal_i(1, s_method_func_imp_call_count);
|
||||
}
|
||||
|
||||
void test_rocky_api_util__error_print(void) {
|
||||
s_log_internal__expected = (const char *[]){
|
||||
"Unhandled Error",
|
||||
" "ERROR_STRING,
|
||||
NULL
|
||||
};
|
||||
|
||||
jerry_value_t error_val =
|
||||
jerry_create_error(JERRY_ERROR_COMMON, (const jerry_char_t *)ERROR_STRING);
|
||||
cl_assert(jerry_value_has_error_flag(error_val));
|
||||
|
||||
// NOTE: prv_log_uncaught_error() will call jerry_release_value(), so don't use error_val after
|
||||
// this call returns:
|
||||
prv_log_uncaught_error(error_val);
|
||||
|
||||
cl_assert(*s_log_internal__expected == NULL);
|
||||
}
|
||||
|
||||
void test_rocky_api_util__call_no_error(void) {
|
||||
s_log_internal__expected = (const char *[]){ NULL };
|
||||
const char *script = "var "FUNC_NAME" = function() { return 1; };";
|
||||
prv_do_call_user_function(script);
|
||||
cl_assert(*s_log_internal__expected == NULL);
|
||||
}
|
||||
|
||||
void test_rocky_api_util__call_throw_string(void) {
|
||||
s_log_internal__expected = (const char *[]){
|
||||
"Unhandled exception",
|
||||
" "ERROR_STRING,
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *script = "var "FUNC_NAME" = function() { throw '"ERROR_STRING"'; };";
|
||||
prv_do_call_user_function(script);
|
||||
cl_assert(*s_log_internal__expected == NULL);
|
||||
}
|
||||
|
||||
void test_rocky_api_util__call_throw_number(void) {
|
||||
s_log_internal__expected = (const char *[]){
|
||||
"Unhandled exception",
|
||||
" 1",
|
||||
NULL
|
||||
};
|
||||
const char *script = "var "FUNC_NAME" = function() { throw 1; };";
|
||||
prv_do_call_user_function(script);
|
||||
|
||||
cl_assert(*s_log_internal__expected == NULL);
|
||||
}
|
||||
|
||||
void test_rocky_api_util__call_throw_error(void) {
|
||||
s_log_internal__expected = (const char *[]){
|
||||
"Unhandled Error",
|
||||
" "ERROR_STRING,
|
||||
NULL
|
||||
};
|
||||
const char *script = "var "FUNC_NAME" = function() { throw new Error('"ERROR_STRING"'); };";
|
||||
prv_do_call_user_function(script);
|
||||
cl_assert(*s_log_internal__expected == NULL);
|
||||
}
|
||||
|
||||
void test_rocky_api_util__eval_no_error(void) {
|
||||
s_log_internal__expected = (const char *[]){ NULL };
|
||||
prv_do_eval("1+1;");
|
||||
cl_assert(*s_log_internal__expected == NULL);
|
||||
}
|
||||
|
||||
void test_rocky_api_util__eval_throw_string(void) {
|
||||
s_log_internal__expected = (const char *[]){
|
||||
"Unhandled exception",
|
||||
" "ERROR_STRING,
|
||||
NULL
|
||||
};
|
||||
prv_do_eval("throw '"ERROR_STRING"';");
|
||||
cl_assert(*s_log_internal__expected == NULL);
|
||||
}
|
||||
|
||||
void test_rocky_api_util__eval_throw_error(void) {
|
||||
s_log_internal__expected = (const char *[]){
|
||||
"Unhandled Error",
|
||||
" "ERROR_STRING,
|
||||
NULL
|
||||
};
|
||||
prv_do_eval("throw new Error('"ERROR_STRING"');");
|
||||
cl_assert(*s_log_internal__expected == NULL);
|
||||
}
|
||||
|
||||
void test_rocky_api_util__create_date_now(void) {
|
||||
const time_t cur_time = 1458250851; // Thu Mar 17 21:40:51 2016 UTC
|
||||
// Thu Mar 17 14:40:51 2016 PDT
|
||||
const uint16_t cur_millis = 123;
|
||||
fake_time_init(cur_time, cur_millis);
|
||||
|
||||
jerry_value_t now = rocky_util_create_date(NULL);
|
||||
jerry_value_t getSeconds = jerry_get_object_field(now, "getSeconds");
|
||||
jerry_value_t getMinutes = jerry_get_object_field(now, "getMinutes");
|
||||
jerry_value_t getHours = jerry_get_object_field(now, "getHours");
|
||||
jerry_value_t getDate = jerry_get_object_field(now, "getDate");
|
||||
|
||||
jerry_value_t result_seconds = jerry_call_function(getSeconds, now, NULL, 0);
|
||||
jerry_value_t result_minutes = jerry_call_function(getMinutes, now, NULL, 0);
|
||||
jerry_value_t result_hours = jerry_call_function(getHours, now, NULL, 0);
|
||||
jerry_value_t result_date = jerry_call_function(getDate, now, NULL, 0);
|
||||
|
||||
cl_assert(jerry_get_number_value(result_seconds) == 51.0);
|
||||
cl_assert(jerry_get_number_value(result_minutes) == 40.0);
|
||||
cl_assert(jerry_get_number_value(result_hours) == 21.0);
|
||||
cl_assert(jerry_get_number_value(result_date) == 17.0);
|
||||
|
||||
jerry_release_value(result_date);
|
||||
jerry_release_value(result_hours);
|
||||
jerry_release_value(result_minutes);
|
||||
jerry_release_value(result_seconds);
|
||||
jerry_release_value(getDate);
|
||||
jerry_release_value(getHours);
|
||||
jerry_release_value(getMinutes);
|
||||
jerry_release_value(getSeconds);
|
||||
jerry_release_value(now);
|
||||
}
|
||||
|
||||
void test_rocky_api_util__ecma_date_make_day(void) {
|
||||
#ifdef EMSCRIPTEN
|
||||
printf("Skipping test %s", __FUNCTION__);
|
||||
#else
|
||||
cl_assert_equal_d(16861, ecma_date_make_day(2016, 2, 1)); // JerryScript's unit-test
|
||||
cl_assert_equal_d(-25294, ecma_date_make_day(1900, 9, 1)); // not a leap year!
|
||||
cl_assert_equal_d(17075, ecma_date_make_day(2016, 8, 31)); // Sept-31 == Oct-01
|
||||
cl_assert_equal_d(17075, ecma_date_make_day(2016, 9, 1)); // Oct-01
|
||||
cl_assert_equal_d(17045, ecma_date_make_day(2016, 8, 1)); // Sept-01
|
||||
#endif // EMSCRIPTEN
|
||||
}
|
||||
|
||||
void test_rocky_api_util__ecma_date_make_day_list(void) {
|
||||
#ifdef EMSCRIPTEN
|
||||
printf("Skipping test %s", __FUNCTION__);
|
||||
#else
|
||||
int fail_count = 0;
|
||||
for(int y = 1950; y < 2050; y++) {
|
||||
for(int m = 0; m < 12; m++) {
|
||||
for (int d = 1; d < 32; d++) {
|
||||
const ecma_number_t result = ecma_date_make_day(y, m, d);
|
||||
if (isnan(result)) {
|
||||
printf("failed for %04d-%02d-%02d\n", y, (m + 1), d);
|
||||
fail_count++;
|
||||
} else {
|
||||
// printf("passed for %04d-%02d-%02d: %d\n", y, (m + 1), d, (int)result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cl_assert_equal_i(0, fail_count);
|
||||
#endif // EMSCRIPTEN
|
||||
}
|
||||
|
||||
void test_rocky_api_util__create_date_tm(void) {
|
||||
const time_t cur_time = 1458250851; // Thu Mar 17 21:40:51 2016 UTC
|
||||
// Thu Mar 17 14:40:51 2016 PDT
|
||||
const uint16_t cur_millis = 123;
|
||||
fake_time_init(cur_time, cur_millis);
|
||||
struct tm tick_time = {
|
||||
.tm_sec = 28,
|
||||
.tm_min = 38,
|
||||
.tm_hour = 18,
|
||||
.tm_mday = 30,
|
||||
.tm_mon = 9,
|
||||
.tm_year = 116,
|
||||
.tm_wday = 1,
|
||||
.tm_yday = 275,
|
||||
.tm_zone = "\000\000\000\000\000",
|
||||
};
|
||||
|
||||
jerry_value_t now = rocky_util_create_date(&tick_time);
|
||||
cl_assert(jerry_value_is_object(now));
|
||||
jerry_value_t getSeconds = jerry_get_object_field(now, "getSeconds");
|
||||
jerry_value_t getMinutes = jerry_get_object_field(now, "getMinutes");
|
||||
jerry_value_t getHours = jerry_get_object_field(now, "getHours");
|
||||
jerry_value_t getDate = jerry_get_object_field(now, "getDate");
|
||||
jerry_value_t getMonth = jerry_get_object_field(now, "getMonth");
|
||||
jerry_value_t getYear = jerry_get_object_field(now, "getYear");
|
||||
|
||||
jerry_value_t result_seconds = jerry_call_function(getSeconds, now, NULL, 0);
|
||||
jerry_value_t result_minutes = jerry_call_function(getMinutes, now, NULL, 0);
|
||||
jerry_value_t result_hours = jerry_call_function(getHours, now, NULL, 0);
|
||||
jerry_value_t result_date = jerry_call_function(getDate, now, NULL, 0);
|
||||
jerry_value_t result_month = jerry_call_function(getMonth, now, NULL, 0);
|
||||
jerry_value_t result_year = jerry_call_function(getYear, now, NULL, 0);
|
||||
|
||||
cl_assert_equal_d(jerry_get_number_value(result_seconds), 28.0);
|
||||
cl_assert_equal_d(jerry_get_number_value(result_minutes), 38.0);
|
||||
cl_assert_equal_d(jerry_get_number_value(result_hours), 18.0);
|
||||
cl_assert_equal_d(jerry_get_number_value(result_date), 30.0);
|
||||
cl_assert_equal_d(jerry_get_number_value(result_month), 9.0);
|
||||
cl_assert_equal_d(jerry_get_number_value(result_year), 116.0);
|
||||
|
||||
jerry_release_value(result_year);
|
||||
jerry_release_value(result_month);
|
||||
jerry_release_value(result_date);
|
||||
jerry_release_value(result_hours);
|
||||
jerry_release_value(result_minutes);
|
||||
jerry_release_value(result_seconds);
|
||||
jerry_release_value(getYear);
|
||||
jerry_release_value(getMonth);
|
||||
jerry_release_value(getDate);
|
||||
jerry_release_value(getHours);
|
||||
jerry_release_value(getMinutes);
|
||||
jerry_release_value(getSeconds);
|
||||
jerry_release_value(now);
|
||||
}
|
620
tests/fw/javascript/test_rocky_api_util_args.c
Normal file
620
tests/fw/javascript/test_rocky_api_util_args.c
Normal file
|
@ -0,0 +1,620 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
#include "applib/rockyjs/api/rocky_api_util_args.h"
|
||||
|
||||
#include "applib/rockyjs/api/rocky_api_errors.h"
|
||||
#include "applib/rockyjs/api/rocky_api_util.h"
|
||||
#include "applib/rockyjs/pbl_jerry_port.h"
|
||||
|
||||
#include "test_jerry_port_common.h"
|
||||
#include "test_rocky_common.h"
|
||||
|
||||
#include <util/size.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Fakes
|
||||
#include "fake_pbl_malloc.h"
|
||||
#include "fake_time.h"
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_sys_exit.h"
|
||||
|
||||
#define JERRY_ARGS_MAKE(...) \
|
||||
jerry_value_t argv[] = { \
|
||||
__VA_ARGS__ \
|
||||
}; \
|
||||
jerry_length_t argc = ARRAY_LENGTH(argv);
|
||||
|
||||
#define JERRY_ARGS_RELEASE() \
|
||||
while(argc--) { \
|
||||
jerry_release_value(argv[argc]); \
|
||||
argv[argc] = 0; \
|
||||
}
|
||||
|
||||
#define ROCKY_ARGS_ASSIGN(...) \
|
||||
const RockyArgBinding bindings[] = { \
|
||||
__VA_ARGS__ \
|
||||
}; \
|
||||
JS_VAR error_value = \
|
||||
rocky_args_assign(argc, argv, bindings, ARRAY_LENGTH(bindings)); \
|
||||
|
||||
|
||||
void test_rocky_api_util_args__initialize(void) {
|
||||
rocky_runtime_context_init();
|
||||
jerry_init(JERRY_INIT_EMPTY);
|
||||
}
|
||||
|
||||
void test_rocky_api_util_args__cleanup(void) {
|
||||
jerry_cleanup();
|
||||
rocky_runtime_context_deinit();
|
||||
fake_pbl_malloc_check_net_allocs(); // Make sure no memory was leaked
|
||||
}
|
||||
|
||||
void test_rocky_api_util_args__missing_args(void) {
|
||||
JERRY_ARGS_MAKE(/* argc == 0 */);
|
||||
|
||||
uint8_t v;
|
||||
ROCKY_ARGS_ASSIGN(ROCKY_ARG(v));
|
||||
ASSERT_JS_ERROR(error_value, "TypeError: Not enough arguments");
|
||||
}
|
||||
|
||||
void test_rocky_api_util_args__numbers_get_rounded_when_converting_to_integer(void) {
|
||||
struct {
|
||||
double input;
|
||||
int16_t expected_output;
|
||||
} cases[] = {
|
||||
{
|
||||
.input = 0.5,
|
||||
.expected_output = 1,
|
||||
},
|
||||
{
|
||||
.input = -0.5,
|
||||
.expected_output = -1,
|
||||
},
|
||||
{
|
||||
.input = 0.0,
|
||||
.expected_output = 0,
|
||||
},
|
||||
{
|
||||
.input = -0.3,
|
||||
.expected_output = 0,
|
||||
},
|
||||
};
|
||||
for (int i = 0; i < ARRAY_LENGTH(cases); ++i) {
|
||||
int16_t output = ~0;
|
||||
jerry_value_t v = jerry_create_number(cases[i].input);
|
||||
JERRY_ARGS_MAKE(v);
|
||||
ROCKY_ARGS_ASSIGN(ROCKY_ARG(output));
|
||||
cl_assert_equal_i(output, cases[i].expected_output);
|
||||
ASSERT_JS_ERROR(error_value, NULL);
|
||||
jerry_release_value(v);
|
||||
}
|
||||
}
|
||||
|
||||
void test_rocky_api_util_args__numbers(void) {
|
||||
uint8_t u8;
|
||||
uint16_t u16;
|
||||
uint32_t u32;
|
||||
uint64_t u64;
|
||||
int8_t s8;
|
||||
int16_t s16;
|
||||
int32_t s32;
|
||||
int64_t s64;
|
||||
double d;
|
||||
|
||||
typedef struct {
|
||||
long double u8;
|
||||
long double u16;
|
||||
long double u32;
|
||||
long double u64;
|
||||
long double s8;
|
||||
long double s16;
|
||||
long double s32;
|
||||
long double s64;
|
||||
double d;
|
||||
char *expected_error_msg;
|
||||
} NumbersTestCase;
|
||||
|
||||
enum {
|
||||
WithinLowerBounds,
|
||||
WithinUpperBounds,
|
||||
UnderLowerBounds,
|
||||
OverUpperBounds,
|
||||
};
|
||||
|
||||
// FIXME: fix limits.h / stdint.h / float.h so we can use the standard defines for these values..
|
||||
NumbersTestCase cases[4] = {
|
||||
[WithinLowerBounds] = {
|
||||
.u8 = 0.0,
|
||||
.u16 = 0.0,
|
||||
.u32 = 0.0,
|
||||
.u64 = 0.0,
|
||||
.s8 = -128.0,
|
||||
.s16 = -32768.0,
|
||||
.s32 = -2147483648.0,
|
||||
.s64 = -9223372036854775808.0,
|
||||
.d = 2.2250738585072014e-308,
|
||||
.expected_error_msg = NULL,
|
||||
},
|
||||
[WithinUpperBounds] = {
|
||||
.u8 = 255.0,
|
||||
.u16 = 65535.0,
|
||||
.u32 = 4294967295.0,
|
||||
.u64 = 9223372036854775807.0,
|
||||
.s8 = 127.0,
|
||||
.s16 = 32767.0,
|
||||
.s32 = 2147483647.0,
|
||||
.s64 = 9223372036854775807.0,
|
||||
.d = 1.7976931348623157e+308,
|
||||
.expected_error_msg = NULL,
|
||||
},
|
||||
};
|
||||
|
||||
const long double margin = 0.001;
|
||||
cases[UnderLowerBounds] = (NumbersTestCase) {
|
||||
.u8 = cases[WithinLowerBounds].u8 - margin,
|
||||
.u16 = cases[WithinLowerBounds].u16 - margin,
|
||||
.u32 = cases[WithinLowerBounds].u32 - margin,
|
||||
.u64 = cases[WithinLowerBounds].u64 - margin,
|
||||
.s8 = cases[WithinLowerBounds].s8 - margin,
|
||||
.s16 = cases[WithinLowerBounds].s16 - margin,
|
||||
.s32 = cases[WithinLowerBounds].s32 - margin,
|
||||
.s64 = cases[WithinLowerBounds].s64 - margin,
|
||||
.d = cases[WithinLowerBounds].d - margin,
|
||||
.expected_error_msg =
|
||||
"TypeError: Argument at index 0 is invalid: Value out of bounds for native type",
|
||||
};
|
||||
cases[OverUpperBounds] = (NumbersTestCase) {
|
||||
.u8 = cases[WithinUpperBounds].u8 + margin,
|
||||
.u16 = cases[WithinUpperBounds].u16 + margin,
|
||||
.u32 = cases[WithinUpperBounds].u32 + margin,
|
||||
.u64 = cases[WithinUpperBounds].u64 + margin,
|
||||
.s8 = cases[WithinUpperBounds].s8 + margin,
|
||||
.s16 = cases[WithinUpperBounds].s16 + margin,
|
||||
.s32 = cases[WithinUpperBounds].s32 + margin,
|
||||
.s64 = cases[WithinUpperBounds].s64 + margin,
|
||||
.d = cases[WithinUpperBounds].d - margin,
|
||||
.expected_error_msg =
|
||||
"TypeError: Argument at index 0 is invalid: Value out of bounds for native type",
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LENGTH(cases); ++i) {
|
||||
NumbersTestCase *c = &cases[i];
|
||||
|
||||
// Initialize to something that's not the expected value:
|
||||
u8 = 1 + (uint8_t)c->u8;
|
||||
u16 = 1 + (uint16_t)c->u16;
|
||||
u32 = 1 + (uint16_t)c->u32;
|
||||
u64 = 1 + (uint16_t)c->u64;
|
||||
s8 = 1 + (int8_t)c->s8;
|
||||
s16 = 1 + (int16_t)c->s16;
|
||||
s32 = 1 + (int16_t)c->s32;
|
||||
s64 = 1 + (int16_t)c->s64;
|
||||
d = 1 + (double)c->d;
|
||||
|
||||
JERRY_ARGS_MAKE(
|
||||
jerry_create_number(c->u8),
|
||||
jerry_create_number(c->u16),
|
||||
jerry_create_number(c->u32),
|
||||
jerry_create_number(c->u64),
|
||||
jerry_create_number(c->s8),
|
||||
jerry_create_number(c->s16),
|
||||
jerry_create_number(c->s32),
|
||||
jerry_create_number(c->s64),
|
||||
jerry_create_number(c->d),
|
||||
);
|
||||
ROCKY_ARGS_ASSIGN(
|
||||
ROCKY_ARG(u8),
|
||||
ROCKY_ARG(u16),
|
||||
ROCKY_ARG(u32),
|
||||
ROCKY_ARG(u64),
|
||||
ROCKY_ARG(s8),
|
||||
ROCKY_ARG(s16),
|
||||
ROCKY_ARG(s32),
|
||||
ROCKY_ARG(s64),
|
||||
ROCKY_ARG(d),
|
||||
);
|
||||
ASSERT_JS_ERROR(error_value, c->expected_error_msg);
|
||||
if (!c->expected_error_msg) {
|
||||
cl_assert_equal_i(u8, (uint8_t)c->u8);
|
||||
cl_assert_equal_i(u16, (uint16_t)c->u16);
|
||||
cl_assert_equal_i(u32, (uint32_t)c->u32);
|
||||
cl_assert_equal_i(u64, (uint64_t)c->u64);
|
||||
cl_assert_equal_i(s8, (int8_t)c->s8);
|
||||
cl_assert_equal_i(s16, (int16_t)c->s16);
|
||||
cl_assert_equal_i(s32, (int32_t)c->s32);
|
||||
cl_assert_equal_i(s64, (int64_t)c->s64);
|
||||
cl_assert_equal_d(d, (double)c->d);
|
||||
}
|
||||
JERRY_ARGS_RELEASE();
|
||||
}
|
||||
}
|
||||
|
||||
void test_rocky_api_util_args__number_type_mismatch(void) {
|
||||
jerry_value_t mismatch_args[] = {
|
||||
jerry_create_null(),
|
||||
jerry_create_string((const jerry_char_t *)"one"),
|
||||
jerry_create_string((const jerry_char_t *)"1"),
|
||||
jerry_create_array(1),
|
||||
jerry_create_boolean(true),
|
||||
jerry_create_object(),
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LENGTH(mismatch_args); ++i) {
|
||||
jerry_value_t arg = mismatch_args[i];
|
||||
JERRY_ARGS_MAKE(arg);
|
||||
|
||||
for (RockyArgType type = RockyArgTypeUInt8; type <= RockyArgTypeDouble; ++type) {
|
||||
uint8_t buffer_untouched[8]; // The type check fails, so nothing is supposed to be written.
|
||||
ROCKY_ARGS_ASSIGN(
|
||||
ROCKY_ARG_MAKE(buffer_untouched, type, {}),
|
||||
);
|
||||
ASSERT_JS_ERROR(error_value, "TypeError: Argument at index 0 is not a Number");
|
||||
}
|
||||
|
||||
jerry_release_value(arg);
|
||||
mismatch_args[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static jerry_value_t prv_dummy(const jerry_value_t function_obj_p,
|
||||
const jerry_value_t this_val,
|
||||
const jerry_value_t args_p[],
|
||||
const jerry_length_t args_count) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test_rocky_api_util_args__boolean(void) {
|
||||
// No API to create NaN :(
|
||||
char *nan_script = "Number.NaN";
|
||||
jerry_value_t nan = jerry_eval((jerry_char_t *)nan_script,
|
||||
strlen(nan_script),
|
||||
false /* is_strict */);
|
||||
|
||||
struct {
|
||||
jerry_value_t input;
|
||||
bool expected_output;
|
||||
} cases[] = {
|
||||
// Falsy: false, 0, "", null, undefined, and NaN:
|
||||
{
|
||||
.input = jerry_create_boolean(false),
|
||||
.expected_output = false,
|
||||
},
|
||||
{
|
||||
.input = jerry_create_number(0.0),
|
||||
.expected_output = false,
|
||||
},
|
||||
{
|
||||
.input = jerry_create_string((const jerry_char_t *)""),
|
||||
.expected_output = false,
|
||||
},
|
||||
{
|
||||
.input = jerry_create_null(),
|
||||
.expected_output = false,
|
||||
},
|
||||
{
|
||||
.input = jerry_create_undefined(),
|
||||
.expected_output = false,
|
||||
},
|
||||
{
|
||||
.input = nan,
|
||||
.expected_output = false,
|
||||
},
|
||||
// Truthy values:
|
||||
{
|
||||
.input = jerry_create_boolean(true),
|
||||
.expected_output = true,
|
||||
},
|
||||
{
|
||||
.input = jerry_create_number(1.0),
|
||||
.expected_output = true,
|
||||
},
|
||||
{
|
||||
.input = jerry_create_string((const jerry_char_t *)" "),
|
||||
.expected_output = true,
|
||||
},
|
||||
{
|
||||
.input = jerry_create_array(0),
|
||||
.expected_output = true,
|
||||
},
|
||||
{
|
||||
.input = jerry_create_object(),
|
||||
.expected_output = true,
|
||||
},
|
||||
{
|
||||
.input = jerry_create_external_function(prv_dummy),
|
||||
.expected_output = true,
|
||||
},
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LENGTH(cases); ++i) {
|
||||
bool output = !cases[i].expected_output;
|
||||
|
||||
jerry_value_t input = cases[i].input;
|
||||
JERRY_ARGS_MAKE(input);
|
||||
|
||||
ROCKY_ARGS_ASSIGN(ROCKY_ARG(output));
|
||||
cl_assert_equal_b(output, cases[i].expected_output);
|
||||
ASSERT_JS_ERROR(error_value, NULL /* Never errors out! */);
|
||||
|
||||
jerry_release_value(input);
|
||||
cases[i].input = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void test_rocky_api_util_args__string(void) {
|
||||
struct {
|
||||
jerry_value_t input;
|
||||
char *expected_output;
|
||||
} cases[] = {
|
||||
{
|
||||
.input = jerry_create_boolean(false),
|
||||
.expected_output = "false",
|
||||
},
|
||||
{
|
||||
.input = jerry_create_number(0.0),
|
||||
.expected_output = "0",
|
||||
},
|
||||
{
|
||||
.input = jerry_create_number(1.234e+60),
|
||||
.expected_output = "1.234e+60",
|
||||
},
|
||||
{
|
||||
.input = jerry_create_string((const jerry_char_t *)""),
|
||||
.expected_output = "",
|
||||
},
|
||||
{
|
||||
.input = jerry_create_string((const jerry_char_t *)"js"),
|
||||
.expected_output = "js",
|
||||
},
|
||||
{
|
||||
.input = jerry_create_null(),
|
||||
.expected_output = "null",
|
||||
},
|
||||
{
|
||||
.input = jerry_create_undefined(),
|
||||
.expected_output = "undefined",
|
||||
},
|
||||
{
|
||||
.input = jerry_create_array(0),
|
||||
.expected_output = "", // Kinda weird?
|
||||
},
|
||||
{
|
||||
.input = jerry_create_object(),
|
||||
.expected_output = "[object Object]",
|
||||
},
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LENGTH(cases); ++i) {
|
||||
jerry_value_t input = cases[i].input;
|
||||
JERRY_ARGS_MAKE(input);
|
||||
|
||||
// Exercise ROCKY_ARG (automatic binding creation, defaults to malloc'd string for char *):
|
||||
{
|
||||
char *output = NULL;
|
||||
ROCKY_ARGS_ASSIGN(ROCKY_ARG(output));
|
||||
cl_assert_equal_s(output, cases[i].expected_output);
|
||||
ASSERT_JS_ERROR(error_value, NULL /* Never errors out! */);
|
||||
task_free(output);
|
||||
}
|
||||
|
||||
// Exercise ROCKY_ARG (automatic binding creation, defaults to copy/no-malloc for char[]):
|
||||
{
|
||||
char output[16];
|
||||
ROCKY_ARGS_ASSIGN(ROCKY_ARG(output));
|
||||
cl_assert_equal_s(output, cases[i].expected_output);
|
||||
ASSERT_JS_ERROR(error_value, NULL /* Never errors out! */);
|
||||
}
|
||||
|
||||
// Exercise ROCKY_ARG_STR (no malloc, explicit binding creation):
|
||||
{
|
||||
char output[16];
|
||||
ROCKY_ARGS_ASSIGN(ROCKY_ARG_STR(output, sizeof(output)));
|
||||
cl_assert_equal_s(output, cases[i].expected_output);
|
||||
ASSERT_JS_ERROR(error_value, NULL /* Never errors out! */);
|
||||
}
|
||||
|
||||
// Exercise ROCKY_ARG_STR (too small buffer provided):
|
||||
{
|
||||
char output[1] = {0xff};
|
||||
ROCKY_ARGS_ASSIGN(ROCKY_ARG_STR(output, 0));
|
||||
cl_assert_equal_s(output, "");
|
||||
ASSERT_JS_ERROR(error_value, NULL /* Never errors out! */);
|
||||
}
|
||||
|
||||
jerry_release_value(input);
|
||||
cases[i].input = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define PP(_x, _y, _w, _h) { \
|
||||
.origin.x.raw_value = (_x), \
|
||||
.origin.y.raw_value = (_y), \
|
||||
.size.w.raw_value = (_w), \
|
||||
.size.h.raw_value = (_h), \
|
||||
}
|
||||
|
||||
void test_rocky_api_util_args__grect_precise(void) {
|
||||
struct {
|
||||
jerry_value_t argv[5];
|
||||
size_t argc;
|
||||
GRectPrecise expected_output;
|
||||
char *error_msg;
|
||||
} cases[] = {
|
||||
{
|
||||
.argv = {
|
||||
[0] = jerry_create_number(0.0),
|
||||
[1] = jerry_create_number(0.0),
|
||||
[2] = jerry_create_number(0.0),
|
||||
[3] = jerry_create_number(0.0),
|
||||
},
|
||||
.argc = 4,
|
||||
.expected_output = PP(0, 0, 0, 0),
|
||||
},
|
||||
{
|
||||
.argv = {
|
||||
[0] = jerry_create_number(-0.5),
|
||||
[1] = jerry_create_number(-0.2),
|
||||
[2] = jerry_create_number(0.3),
|
||||
[3] = jerry_create_number(0.5),
|
||||
},
|
||||
.argc = 4,
|
||||
.expected_output = PP(-4, -2, 2, 4),
|
||||
},
|
||||
{
|
||||
.argv = {
|
||||
[0] = jerry_create_number(-4096.0),
|
||||
[1] = jerry_create_number(-4096.0),
|
||||
[2] = jerry_create_number(4095.875),
|
||||
[3] = jerry_create_number(4095.875),
|
||||
},
|
||||
.argc = 4,
|
||||
.expected_output = PP(-32768, -32768, 32767, 32767),
|
||||
},
|
||||
{
|
||||
.argv = {
|
||||
[0] = jerry_create_number(0),
|
||||
[1] = jerry_create_number(0),
|
||||
[2] = jerry_create_number(0),
|
||||
[3] = jerry_create_number(4096.0),
|
||||
},
|
||||
.argc = 4,
|
||||
.error_msg = "TypeError: Argument at index 3 is invalid: Value out of bounds for native type",
|
||||
},
|
||||
{
|
||||
.argv = {
|
||||
[0] = jerry_create_number(0),
|
||||
[1] = jerry_create_number(0),
|
||||
[2] = jerry_create_number(0),
|
||||
},
|
||||
.argc = 3,
|
||||
.error_msg = "TypeError: Not enough arguments",
|
||||
},
|
||||
{
|
||||
.argv = {
|
||||
[0] = jerry_create_number(0),
|
||||
[1] = jerry_create_number(0),
|
||||
[2] = jerry_create_number(0),
|
||||
[3] = jerry_create_null(),
|
||||
},
|
||||
.argc = 4,
|
||||
.error_msg = "TypeError: Argument at index 3 is not a Number",
|
||||
},
|
||||
{
|
||||
.argv = {
|
||||
[0] = jerry_create_number(0),
|
||||
[1] = jerry_create_number(0),
|
||||
[2] = jerry_create_number(0),
|
||||
[3] = jerry_create_string((const jerry_char_t *)"123"),
|
||||
},
|
||||
.argc = 4,
|
||||
.error_msg = "TypeError: Argument at index 3 is not a Number",
|
||||
},
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LENGTH(cases); ++i) {
|
||||
jerry_value_t *argv = cases[i].argv;
|
||||
jerry_length_t argc = cases[i].argc;
|
||||
|
||||
GRectPrecise output;
|
||||
memset(&output, 0x55, sizeof(output));
|
||||
|
||||
ROCKY_ARGS_ASSIGN(ROCKY_ARG(output));
|
||||
ASSERT_JS_ERROR(error_value, cases[i].error_msg);
|
||||
if (!cases[i].error_msg) {
|
||||
cl_assert_equal_i(output.origin.x.raw_value, cases[i].expected_output.origin.x.raw_value);
|
||||
cl_assert_equal_i(output.origin.y.raw_value, cases[i].expected_output.origin.y.raw_value);
|
||||
cl_assert_equal_i(output.size.w.raw_value, cases[i].expected_output.size.w.raw_value);
|
||||
cl_assert_equal_i(output.size.h.raw_value, cases[i].expected_output.size.h.raw_value);
|
||||
}
|
||||
|
||||
for (uint32_t j = 0; j < argc; ++j) {
|
||||
jerry_release_value(argv[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_rocky_api_util_args__gcolor(void) {
|
||||
const char *type_error_msg =
|
||||
"TypeError: Argument at index 0 is not a String ('color name' or '#hex') or Number";
|
||||
const char *invalid_value_msg =
|
||||
"TypeError: Argument at index 0 is invalid: " \
|
||||
"Expecting String ('color name' or '#hex') or Number";
|
||||
struct {
|
||||
jerry_value_t input;
|
||||
GColor expected_output;
|
||||
const char *error_msg;
|
||||
} cases[] = {
|
||||
{
|
||||
.input = jerry_create_number(0.0),
|
||||
.expected_output = {
|
||||
.argb = 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
.input = jerry_create_number(GColorJaegerGreenARGB8),
|
||||
.expected_output = {
|
||||
.argb = GColorJaegerGreenARGB8,
|
||||
}
|
||||
},
|
||||
{
|
||||
.input = jerry_create_string((const jerry_char_t *)"red"),
|
||||
.expected_output = {
|
||||
.r = 0b11,
|
||||
.g = 0,
|
||||
.b = 0,
|
||||
.a = 0b11,
|
||||
}
|
||||
},
|
||||
{
|
||||
.input = jerry_create_string((const jerry_char_t *)"unknown-color"),
|
||||
.error_msg = invalid_value_msg,
|
||||
},
|
||||
{
|
||||
.input = jerry_create_null(),
|
||||
.error_msg = type_error_msg,
|
||||
},
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAY_LENGTH(cases); ++i) {
|
||||
jerry_value_t input = cases[i].input;
|
||||
JERRY_ARGS_MAKE(input);
|
||||
|
||||
GColor output;
|
||||
memset(&output, 0x55, sizeof(output));
|
||||
|
||||
ROCKY_ARGS_ASSIGN(ROCKY_ARG(output));
|
||||
ASSERT_JS_ERROR(error_value, cases[i].error_msg);
|
||||
if (!cases[i].error_msg) {
|
||||
cl_assert_equal_i(output.a, cases[i].expected_output.a);
|
||||
cl_assert_equal_i(output.r, cases[i].expected_output.r);
|
||||
cl_assert_equal_i(output.g, cases[i].expected_output.g);
|
||||
cl_assert_equal_i(output.b, cases[i].expected_output.b);
|
||||
}
|
||||
|
||||
jerry_release_value(cases[i].input);
|
||||
cases[i].input = 0;
|
||||
}
|
||||
}
|
158
tests/fw/javascript/test_rocky_api_watchinfo.c
Normal file
158
tests/fw/javascript/test_rocky_api_watchinfo.c
Normal file
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
#include "test_jerry_port_common.h"
|
||||
#include "test_rocky_common.h"
|
||||
|
||||
#include "applib/rockyjs/api/rocky_api_global.h"
|
||||
#include "applib/rockyjs/api/rocky_api_watchinfo.h"
|
||||
#include "applib/rockyjs/pbl_jerry_port.h"
|
||||
|
||||
#include "applib/app_watch_info.h"
|
||||
#include "system/version.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
// Fakes
|
||||
#include "fake_app_timer.h"
|
||||
#include "fake_time.h"
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_sys_exit.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Fakes / Stubs
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define TEST_LOCALE "test_locale"
|
||||
#define VERSION_PREFIX "v4.0"
|
||||
#define VERSION_SUFFIX "beta5"
|
||||
#define VERSION_TAG VERSION_PREFIX"-"VERSION_SUFFIX
|
||||
#define VERSION_MAJOR 4
|
||||
#define VERSION_MINOR 0
|
||||
#define VERSION_PATCH 122
|
||||
|
||||
char *app_get_system_locale(void) {
|
||||
return TEST_LOCALE;
|
||||
}
|
||||
|
||||
bool version_copy_running_fw_metadata(FirmwareMetadata *out_metadata) {
|
||||
strncpy(out_metadata->version_tag, VERSION_TAG, FW_METADATA_VERSION_TAG_BYTES);
|
||||
return true;
|
||||
}
|
||||
|
||||
WatchInfoVersion watch_info_get_firmware_version(void) {
|
||||
return (WatchInfoVersion) {
|
||||
.major = VERSION_MAJOR,
|
||||
.minor = VERSION_MINOR,
|
||||
.patch = VERSION_PATCH
|
||||
};
|
||||
}
|
||||
|
||||
static WatchInfoColor s_watch_info_color;
|
||||
WatchInfoColor sys_watch_info_get_color(void) {
|
||||
return s_watch_info_color;
|
||||
}
|
||||
|
||||
static WatchInfoModel s_watch_info_model;
|
||||
WatchInfoModel watch_info_get_model(void) {
|
||||
return s_watch_info_model;
|
||||
}
|
||||
|
||||
static PlatformType s_current_app_sdk_platform;
|
||||
PlatformType sys_get_current_app_sdk_platform(void) {
|
||||
return s_current_app_sdk_platform;
|
||||
}
|
||||
|
||||
|
||||
static const RockyGlobalAPI *s_watchinfo_api[] = {
|
||||
&WATCHINFO_APIS,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void test_rocky_api_watchinfo__initialize(void) {
|
||||
rocky_runtime_context_init();
|
||||
jerry_init(JERRY_INIT_EMPTY);
|
||||
s_watch_info_model = WATCH_INFO_MODEL_PEBBLE_TIME_STEEL;
|
||||
s_watch_info_color = WATCH_INFO_COLOR_TIME_STEEL_GOLD;
|
||||
s_current_app_sdk_platform = PlatformTypeBasalt;
|
||||
}
|
||||
|
||||
void test_rocky_api_watchinfo__cleanup(void) {
|
||||
if (app_state_get_rocky_runtime_context() != NULL) {
|
||||
jerry_cleanup();
|
||||
rocky_runtime_context_deinit();
|
||||
}
|
||||
}
|
||||
|
||||
void test_rocky_api_watchinfo__model(void) {
|
||||
rocky_global_init(s_watchinfo_api);
|
||||
|
||||
EXECUTE_SCRIPT("var model = _rocky.watchInfo.model");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("model", "pebble_time_steel_gold");
|
||||
}
|
||||
|
||||
void test_rocky_api_watchinfo__qemu_model(void) {
|
||||
s_watch_info_color = -1;
|
||||
rocky_global_init(s_watchinfo_api);
|
||||
|
||||
EXECUTE_SCRIPT("var model = _rocky.watchInfo.model");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("model", "qemu_platform_basalt");
|
||||
}
|
||||
|
||||
void test_rocky_api_watchinfo__language(void) {
|
||||
rocky_global_init(s_watchinfo_api);
|
||||
|
||||
EXECUTE_SCRIPT("var language = _rocky.watchInfo.language");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("language", TEST_LOCALE);
|
||||
}
|
||||
|
||||
void test_rocky_api_watchinfo__platform(void) {
|
||||
rocky_global_init(s_watchinfo_api);
|
||||
|
||||
EXECUTE_SCRIPT("var platform = _rocky.watchInfo.platform");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("platform", "basalt");
|
||||
}
|
||||
|
||||
void test_rocky_api_watchinfo__platform_unknown(void) {
|
||||
s_current_app_sdk_platform = -1; // Some unknown / invalid
|
||||
rocky_global_init(s_watchinfo_api);
|
||||
|
||||
EXECUTE_SCRIPT("var platform = _rocky.watchInfo.platform");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("platform", "unknown");
|
||||
}
|
||||
|
||||
void test_rocky_api_watchinfo__fw_version(void) {
|
||||
rocky_global_init(s_watchinfo_api);
|
||||
|
||||
EXECUTE_SCRIPT("var major = _rocky.watchInfo.firmware.major");
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("major", VERSION_MAJOR);
|
||||
|
||||
EXECUTE_SCRIPT("var minor = _rocky.watchInfo.firmware.minor");
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("minor", VERSION_MINOR);
|
||||
|
||||
EXECUTE_SCRIPT("var patch = _rocky.watchInfo.firmware.patch");
|
||||
ASSERT_JS_GLOBAL_EQUALS_I("patch", VERSION_PATCH);
|
||||
|
||||
EXECUTE_SCRIPT("var suffix = _rocky.watchInfo.firmware.suffix");
|
||||
ASSERT_JS_GLOBAL_EQUALS_S("suffix", VERSION_SUFFIX);
|
||||
}
|
296
tests/fw/javascript/test_rocky_common.h
Normal file
296
tests/fw/javascript/test_rocky_common.h
Normal file
|
@ -0,0 +1,296 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "applib/graphics/gcontext.h"
|
||||
#include "applib/graphics/graphics_circle.h"
|
||||
#include "applib/rockyjs/api/rocky_api_util.h"
|
||||
#include "applib/tick_timer_service.h"
|
||||
#include "applib/ui/layer.h"
|
||||
|
||||
#include "kernel/events.h"
|
||||
#include "syscall/syscall.h"
|
||||
|
||||
#include <clar.h>
|
||||
#include <util/attributes.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define ASSERT_JS_GLOBAL_EQUALS_B(name, value) \
|
||||
cl_assert_equal_i(prv_js_global_get_boolean(name), value);
|
||||
|
||||
#define ASSERT_JS_GLOBAL_EQUALS_I(name, value) \
|
||||
cl_assert_equal_i(prv_js_global_get_double(name), value);
|
||||
|
||||
#define ASSERT_JS_GLOBAL_EQUALS_D(name, value) \
|
||||
cl_assert_equal_d(prv_js_global_get_double(name), value);
|
||||
|
||||
#define ASSERT_JS_GLOBAL_EQUALS_S(name, value) \
|
||||
do { \
|
||||
char str_buffer[1024] = {}; \
|
||||
prv_js_global_get_string(name, str_buffer, sizeof(str_buffer)); \
|
||||
cl_assert_equal_s(str_buffer, value); \
|
||||
} while(0)
|
||||
|
||||
#define JS_GLOBAL_GET_VALUE(name) \
|
||||
prv_js_global_get_value(name)
|
||||
|
||||
#define ASSERT_JS_ERROR(error_value, expected_error_string) \
|
||||
do { \
|
||||
const bool expects_error = (expected_error_string) != NULL; \
|
||||
const bool is_error = jerry_value_has_error_flag(error_value); \
|
||||
if (is_error) { \
|
||||
if (expects_error) { \
|
||||
jerry_char_t buffer[100] = {0}; \
|
||||
jerry_object_to_string_to_utf8_char_buffer(error_value, buffer, sizeof(buffer)); \
|
||||
cl_assert_equal_s((char *)(expected_error_string), (char *)buffer); \
|
||||
} else {\
|
||||
rocky_log_exception("ASSERT_JS_ERROR", error_value); \
|
||||
cl_fail("Error value while no error was expected!"); \
|
||||
} \
|
||||
} else { \
|
||||
if (expects_error) { \
|
||||
cl_fail("expected error during JS execution did not occur"); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define EXECUTE_SCRIPT_EXPECT_UNDEFINED(script) \
|
||||
do { \
|
||||
JS_VAR rv = jerry_eval((jerry_char_t *)script, \
|
||||
strlen(script), \
|
||||
false /* is_strict */); \
|
||||
cl_assert_equal_b(true, jerry_value_is_undefined(rv)); \
|
||||
} while(0)
|
||||
|
||||
|
||||
#define EXECUTE_SCRIPT_EXPECT_ERROR(script, expected_error) \
|
||||
do { \
|
||||
JS_VAR rv = jerry_eval((jerry_char_t *)script, \
|
||||
strlen(script), \
|
||||
false /* is_strict */); \
|
||||
ASSERT_JS_ERROR(rv, expected_error); \
|
||||
} while(0)
|
||||
|
||||
#define EXECUTE_SCRIPT(script) EXECUTE_SCRIPT_EXPECT_ERROR((script), NULL)
|
||||
|
||||
#define EXECUTE_SCRIPT_AND_ASSERT_RV_EQUALS_S(script, expected_c_string) \
|
||||
do { \
|
||||
JS_VAR rv = jerry_eval((jerry_char_t *)script, \
|
||||
strlen(script), false /* is_strict */); \
|
||||
ASSERT_JS_ERROR(rv, NULL); \
|
||||
JS_VAR rv_string = jerry_value_to_string(rv); \
|
||||
jerry_size_t sz = jerry_get_utf8_string_size(rv_string); \
|
||||
cl_assert(sz); \
|
||||
jerry_char_t *buffer = malloc(sz + 1); \
|
||||
memset(buffer, 0, sz + 1); \
|
||||
cl_assert(buffer); \
|
||||
cl_assert_equal_i(sz, jerry_string_to_utf8_char_buffer(rv_string, buffer, sz)); \
|
||||
cl_assert_equal_s((char *)buffer, expected_c_string); \
|
||||
free(buffer); \
|
||||
} while(0)
|
||||
|
||||
#ifndef DO_NOT_STUB_LEGACY2
|
||||
|
||||
UNUSED bool process_manager_compiled_with_legacy2_sdk(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
UNUSED static jerry_value_t prv_js_global_get_value(char *name) {
|
||||
JS_VAR global_obj = jerry_get_global_object();
|
||||
cl_assert_equal_b(jerry_value_is_undefined(global_obj), false);
|
||||
|
||||
JS_VAR val = jerry_get_object_field(global_obj, name);
|
||||
cl_assert_equal_b(jerry_value_is_undefined(val), false);
|
||||
return jerry_acquire_value(val);
|
||||
}
|
||||
|
||||
UNUSED static bool prv_js_global_get_boolean(char *name) {
|
||||
JS_VAR val = prv_js_global_get_value(name);
|
||||
cl_assert(jerry_value_is_boolean(val));
|
||||
double rv = jerry_get_boolean_value(val);
|
||||
return rv;
|
||||
}
|
||||
|
||||
UNUSED static double prv_js_global_get_double(char *name) {
|
||||
JS_VAR val = prv_js_global_get_value(name);
|
||||
cl_assert(jerry_value_is_number(val));
|
||||
double rv = jerry_get_number_value(val);
|
||||
return rv;
|
||||
}
|
||||
|
||||
UNUSED static void prv_js_global_get_string(char *name, char *buffer, size_t buffer_size) {
|
||||
JS_VAR val = prv_js_global_get_value(name);
|
||||
cl_assert(jerry_value_is_string(val));
|
||||
ssize_t num_bytes = jerry_string_to_char_buffer(val, (jerry_char_t *)buffer, buffer_size);
|
||||
cl_assert(num_bytes <= buffer_size);
|
||||
}
|
||||
|
||||
void (*s_app_event_loop_callback)(void);
|
||||
|
||||
void app_event_loop_common(void) {
|
||||
if (s_app_event_loop_callback) {
|
||||
s_app_event_loop_callback();
|
||||
}
|
||||
}
|
||||
|
||||
#define cl_assert_equal_point(a, b) \
|
||||
do { \
|
||||
const GPoint __pt_a = (a); \
|
||||
const GPoint __pt_b = (b); \
|
||||
cl_assert_equal_i(__pt_a.x, __pt_b.x); \
|
||||
cl_assert_equal_i(__pt_a.y, __pt_b.y); \
|
||||
} while(0)
|
||||
|
||||
#define cl_assert_equal_point_precise(a, b) \
|
||||
do { \
|
||||
const GPointPrecise __pt_a = (a); \
|
||||
const GPointPrecise __pt_b = (b); \
|
||||
cl_assert_equal_i(__pt_a.x.raw_value, __pt_b.x.raw_value); \
|
||||
cl_assert_equal_i(__pt_a.y.raw_value, __pt_b.y.raw_value); \
|
||||
} while(0)
|
||||
|
||||
#define cl_assert_equal_vector_precise(a, b) \
|
||||
do { \
|
||||
const GVectorPrecise __a = (a); \
|
||||
const GVectorPrecise __b = (b); \
|
||||
cl_assert_equal_i(__a.dx.raw_value, __b.dx.raw_value); \
|
||||
cl_assert_equal_i(__a.dy.raw_value, __b.dy.raw_value); \
|
||||
} while(0)
|
||||
|
||||
#define cl_assert_equal_size(a, b) \
|
||||
do { \
|
||||
const GSize __sz_a = (a); \
|
||||
const GSize __sz_b = (b); \
|
||||
cl_assert_equal_i(__sz_a.w, __sz_b.w); \
|
||||
cl_assert_equal_i(__sz_a.h, __sz_b.h); \
|
||||
} while(0)
|
||||
|
||||
#define cl_assert_equal_size_precise(a, b) \
|
||||
do { \
|
||||
const GSizePrecise __sz_a = (a); \
|
||||
const GSizePrecise __sz_b = (b); \
|
||||
cl_assert_equal_i(__sz_a.w.raw_value, __sz_b.w.raw_value); \
|
||||
cl_assert_equal_i(__sz_a.h.raw_value, __sz_b.h.raw_value); \
|
||||
} while(0)
|
||||
|
||||
#define cl_assert_equal_rect(a, b) \
|
||||
do { \
|
||||
const GRect __a = (a); \
|
||||
const GRect __b = (b); \
|
||||
cl_assert_equal_point(__a.origin, __b.origin); \
|
||||
cl_assert_equal_size(__a.size, __b.size); \
|
||||
} while(0)
|
||||
|
||||
#define cl_assert_equal_rect_precise(a, b) \
|
||||
do { \
|
||||
const GRectPrecise __a = (a); \
|
||||
const GRectPrecise __b = (b); \
|
||||
cl_assert_equal_point_precise(__a.origin, __b.origin); \
|
||||
cl_assert_equal_size_precise(__a.size, __b.size); \
|
||||
} while(0)
|
||||
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
Layer *layer;
|
||||
GContext *ctx;
|
||||
};
|
||||
union {
|
||||
GColor color;
|
||||
uint8_t width;
|
||||
struct {
|
||||
GPoint p0;
|
||||
GPoint p1;
|
||||
};
|
||||
struct {
|
||||
GPointPrecise pp0;
|
||||
GPointPrecise pp1;
|
||||
};
|
||||
struct {
|
||||
GPointPrecise center;
|
||||
Fixed_S16_3 radius;
|
||||
int32_t angle_start;
|
||||
int32_t angle_end;
|
||||
} draw_arc;
|
||||
struct {
|
||||
GRect rect;
|
||||
uint16_t radius;
|
||||
GCornerMask corner_mask;
|
||||
};
|
||||
struct {
|
||||
GRectPrecise prect;
|
||||
};
|
||||
TimeUnits tick_units;
|
||||
const char *font_key;
|
||||
struct {
|
||||
char text[200];
|
||||
GRect box;
|
||||
GColor color;
|
||||
} draw_text;
|
||||
struct {
|
||||
char text[200];
|
||||
GFont font;
|
||||
GRect box;
|
||||
GTextOverflowMode overflow_mode;
|
||||
GTextAlignment alignment;
|
||||
} max_used_size;
|
||||
struct {
|
||||
GPoint points[200];
|
||||
size_t num_points;
|
||||
} path;
|
||||
struct {
|
||||
GPointPrecise center;
|
||||
Fixed_S16_3 radius_inner;
|
||||
Fixed_S16_3 radius_outer;
|
||||
int32_t angle_start;
|
||||
int32_t angle_end;
|
||||
} fill_radial_precise;
|
||||
};
|
||||
} MockCallRecording;
|
||||
|
||||
typedef struct {
|
||||
int call_count;
|
||||
MockCallRecording last_call;
|
||||
} MockCallRecordings;
|
||||
|
||||
#define record_mock_call(var) \
|
||||
var.call_count++; \
|
||||
var.last_call = (MockCallRecording)
|
||||
|
||||
// Handy for poking at .js things when debugging a unit test with gdb, for example:
|
||||
// (gdb) call js_eval("1 + 1")
|
||||
// 2
|
||||
// (gdb) call js_eval("Date()")
|
||||
// Thu Jan 01 1970 00:00:00 GMT+00:00
|
||||
void js_eval(const char *src) {
|
||||
JS_VAR rv = jerry_eval((const jerry_char_t *)src, strlen(src), false);
|
||||
char buf[256] = {};
|
||||
jerry_object_to_string_to_utf8_char_buffer(rv, (jerry_char_t *)buf, sizeof(buf));
|
||||
printf("%s\n", buf);
|
||||
}
|
||||
|
||||
static void (*s_process_manager_callback)(void *data);
|
||||
static void *s_process_manager_callback_data;
|
||||
void sys_current_process_schedule_callback(CallbackEventCallback async_cb,
|
||||
void *ctx) {
|
||||
s_process_manager_callback = async_cb;
|
||||
s_process_manager_callback_data = ctx;
|
||||
}
|
121
tests/fw/javascript/test_rocky_res.c
Normal file
121
tests/fw/javascript/test_rocky_res.c
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "clar.h"
|
||||
|
||||
#include "test_jerry_port_common.h"
|
||||
|
||||
#include "applib/rockyjs/rocky.h"
|
||||
#include "applib/rockyjs/rocky_res.h"
|
||||
|
||||
// Fakes
|
||||
#include "fake_app_timer.h"
|
||||
#include "fake_time.h"
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_manager.h"
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
#include "stubs_pbl_malloc.h"
|
||||
#include "stubs_sleep.h"
|
||||
#include "stubs_serial.h"
|
||||
#include "stubs_syscalls.h"
|
||||
#include "stubs_sys_exit.h"
|
||||
|
||||
|
||||
// instead of including internal jerry script headers here and pulling the whole dependency we will
|
||||
// duplicate this value here instead
|
||||
// if this fails, duplicate value from src/fw/vendor/jerryscript/jerry-core/jerry-snapshot.h
|
||||
#define JERRY_SNAPSHOT_VERSION (6u)
|
||||
|
||||
void app_event_loop_common(void) {}
|
||||
|
||||
bool sys_get_current_app_is_rocky_app(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t heap_bytes_free(void) {
|
||||
return 123456;
|
||||
}
|
||||
|
||||
static uint32_t s_resource_storage_get_num_entries__result;
|
||||
uint32_t resource_storage_get_num_entries(ResAppNum app_num, uint32_t resource_id) {
|
||||
return s_resource_storage_get_num_entries__result;
|
||||
}
|
||||
|
||||
void rocky_api_watchface_init(void){}
|
||||
void rocky_api_deinit(void){}
|
||||
|
||||
size_t resource_size(ResAppNum app_num, uint32_t id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool resource_is_valid(ResAppNum app_num, uint32_t resource_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int process_metadata_get_res_bank_num(const PebbleProcessMd *md) {
|
||||
return 123;
|
||||
}
|
||||
|
||||
size_t resource_load_byte_range_system(ResAppNum app_num, uint32_t id, uint32_t start_offset, uint8_t *data, size_t num_bytes) {
|
||||
const size_t some_bytes = 20; // a real snapshot is larger than just the header
|
||||
|
||||
// in our test setup, we will treat resource
|
||||
// 10 as an invalid snapshot header, and
|
||||
// 20 as a valid one
|
||||
switch (id) {
|
||||
case 10: {
|
||||
cl_assert(num_bytes >= sizeof(RockySnapshotHeader));
|
||||
*(RockySnapshotHeader*)data = (RockySnapshotHeader) {
|
||||
.version = 123, // invalid
|
||||
};
|
||||
return sizeof(RockySnapshotHeader);
|
||||
}
|
||||
case 20: {
|
||||
const size_t result = sizeof(RockySnapshotHeader) + sizeof(uint64_t);
|
||||
cl_assert(num_bytes >= result);
|
||||
|
||||
RockySnapshotHeader *header = (RockySnapshotHeader*)data;
|
||||
*header = ROCKY_EXPECTED_SNAPSHOT_HEADER;
|
||||
// first uint64_t after our header is the jerry script buffer which starts with a version
|
||||
*(uint64_t*)(header + 1) = JERRY_SNAPSHOT_VERSION;
|
||||
return result;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void test_rocky_res__initialize(void) {
|
||||
s_resource_storage_get_num_entries__result = 0;
|
||||
}
|
||||
|
||||
void test_rocky_res__no_snapshot(void) {
|
||||
s_resource_storage_get_num_entries__result = 5;
|
||||
cl_assert_equal_b(false, rocky_app_has_compatible_bytecode_res(123));
|
||||
}
|
||||
|
||||
void test_rocky_res__only_invalid_snapshot(void) {
|
||||
s_resource_storage_get_num_entries__result = 15;
|
||||
cl_assert_equal_b(false, rocky_app_has_compatible_bytecode_res(123));
|
||||
}
|
||||
|
||||
void test_rocky_res__valid_snapshot(void) {
|
||||
s_resource_storage_get_num_entries__result = 25;
|
||||
cl_assert_equal_b(true, rocky_app_has_compatible_bytecode_res(123));
|
||||
}
|
220
tests/fw/javascript/test_rocky_text_encoding.c
Normal file
220
tests/fw/javascript/test_rocky_text_encoding.c
Normal file
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "test_jerry_port_common.h"
|
||||
#include "test_rocky_common.h"
|
||||
#include "applib/rockyjs/pbl_jerry_port.h"
|
||||
|
||||
#include "jerry-api.h"
|
||||
|
||||
#include <util/size.h>
|
||||
|
||||
#include <clar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Fakes
|
||||
#include "fake_time.h"
|
||||
#include "fake_logging.h"
|
||||
#include "fake_pbl_malloc.h"
|
||||
|
||||
// Stubs
|
||||
#include "stubs_app_state.h"
|
||||
#include "stubs_logging.h"
|
||||
#include "stubs_passert.h"
|
||||
|
||||
// Great read-up on JavaScript and its text encoding quirks:
|
||||
// https://mathiasbynens.be/notes/javascript-unicode
|
||||
|
||||
void test_rocky_text_encoding__initialize(void) {
|
||||
fake_pbl_malloc_clear_tracking();
|
||||
rocky_runtime_context_init();
|
||||
jerry_init(JERRY_INIT_EMPTY);
|
||||
}
|
||||
|
||||
void test_rocky_text_encoding__cleanup(void) {
|
||||
jerry_cleanup();
|
||||
rocky_runtime_context_deinit();
|
||||
fake_pbl_malloc_check_net_allocs();
|
||||
}
|
||||
|
||||
void test_rocky_text_encoding__jerry_handles_cesu8_strings_in_source(void) {
|
||||
// Although CESU-8 and UTF-8 are not compatible on paper, JerryScript's lexer doesn't mind if
|
||||
// we feed it CESU-8 encoded strings... Test this, so we know when this changes in the future:
|
||||
EXECUTE_SCRIPT("var pileOfPooCESU8 = '\xed\xa0\xbd\xed\xb2\xa9';");
|
||||
// Expect a pair of surrogate code points:
|
||||
EXECUTE_SCRIPT_AND_ASSERT_RV_EQUALS_S("pileOfPooCESU8.charCodeAt(0).toString(16)", "d83d");
|
||||
EXECUTE_SCRIPT_AND_ASSERT_RV_EQUALS_S("pileOfPooCESU8.charCodeAt(1).toString(16)", "dca9");
|
||||
}
|
||||
|
||||
void test_rocky_text_encoding__jerry_handles_utf8_strings_in_source(void) {
|
||||
// Source is be UTF-8 encoded.
|
||||
// Have a string variable with Pile of Poo (💩) or U+1F4A9 in it, encoded using 4-bytes:
|
||||
EXECUTE_SCRIPT("var pileOfPooUTF8 = '\xF0\x9F\x92\xA9';");
|
||||
// Expect a pair of surrogate code points:
|
||||
EXECUTE_SCRIPT_AND_ASSERT_RV_EQUALS_S("pileOfPooUTF8.charCodeAt(0).toString(16)", "d83d");
|
||||
EXECUTE_SCRIPT_AND_ASSERT_RV_EQUALS_S("pileOfPooUTF8.charCodeAt(1).toString(16)", "dca9");
|
||||
}
|
||||
|
||||
void test_rocky_text_encoding__jerry_asserts_utf8_non_bmp_codepoint_in_identifier(void) {
|
||||
// It's forbidden to have an identifier contain a non-BMP codepoint (UTF-8 encoded):
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR("var poo\xF0\x9F\x92\xA9poo = 'pileOfPoo';",
|
||||
"SyntaxError: Invalid (unexpected) character. [line: 1, column: 8]");
|
||||
}
|
||||
|
||||
void test_rocky_text_encoding__jerry_asserts_cesu8_non_bmp_codepoint_in_identifier(void) {
|
||||
// It's forbidden to have an identifier contain a non-BMP codepoint (CESU-8 encoded):
|
||||
EXECUTE_SCRIPT_EXPECT_ERROR("var poo\xed\xa0\xbd\xed\xb2\xa9poo = 'pileOfPoo';",
|
||||
"SyntaxError: Invalid (unexpected) character. [line: 1, column: 8]");
|
||||
}
|
||||
|
||||
void test_rocky_text_encoding__string_length(void) {
|
||||
EXECUTE_SCRIPT("var pileOfPooUTF8 = '\xF0\x9F\x92\xA9';");
|
||||
// String.length is expected to count the surrogate code points that make up a non-BMP codepoint:
|
||||
EXECUTE_SCRIPT_AND_ASSERT_RV_EQUALS_S("pileOfPooUTF8.length.toString()", "2");
|
||||
}
|
||||
|
||||
void test_rocky_text_encoding__jerry_cesu8_to_utf8_conversion(void) {
|
||||
struct {
|
||||
const char *const script;
|
||||
size_t expected_utf_size;
|
||||
const char *const expected_utf_data;
|
||||
} cases[] = {
|
||||
[0] = {
|
||||
.script = "var str = '\\uDCA9';", // low surrogate only
|
||||
.expected_utf_size = 0,
|
||||
},
|
||||
[1] = {
|
||||
.script = "var str = '\\uD83D';", // high surrogate only
|
||||
.expected_utf_size = 0,
|
||||
},
|
||||
[2] = {
|
||||
.script = "var str = '\\uDCA9\\uD83D';", // reversed order
|
||||
.expected_utf_size = 0,
|
||||
},
|
||||
[3] = {
|
||||
.script = "var str = '\\uD83Dx\\uDCA9';", // non-surrogate in between pair
|
||||
.expected_utf_size = 1,
|
||||
.expected_utf_data = "x",
|
||||
},
|
||||
[4] = {
|
||||
.script = "var str = '\\uD83Dx';", // high surrogate followed by non-surrogate
|
||||
.expected_utf_size = 1,
|
||||
.expected_utf_data = "x",
|
||||
},
|
||||
[5] = {
|
||||
.script = "var str = '\\uDCA9x';", // low surrogate followed by non-surrogate
|
||||
.expected_utf_size = 1,
|
||||
.expected_utf_data = "x",
|
||||
},
|
||||
[6] = {
|
||||
.script = "var str = 'AB';",
|
||||
.expected_utf_size = 2,
|
||||
.expected_utf_data = "AB",
|
||||
},
|
||||
[7] = {
|
||||
.script = "var str = '\xC4\x91';", // 2-byte codepoint (U+0111)
|
||||
.expected_utf_size = 2,
|
||||
.expected_utf_data = "\xC4\x91",
|
||||
},
|
||||
[8] = {
|
||||
.script = "var str = '\xE0\xA0\x95';", // 3-byte codepoint (U+0815)
|
||||
.expected_utf_size = 3,
|
||||
.expected_utf_data = "\xE0\xA0\x95",
|
||||
},
|
||||
[9] = {
|
||||
.script = "var str = '\\uD83D\\uDCA9';", // 4-byte codepoint (U+1F4A9, escaped data)
|
||||
.expected_utf_size = 4,
|
||||
.expected_utf_data = "\xF0\x9F\x92\xA9",
|
||||
},
|
||||
[10] = {
|
||||
.script = "var str = '\xF0\x9F\x92\xA9';", // 4-byte codepoint (U+1F4A9, UTF-8 data in source)
|
||||
.expected_utf_size = 4,
|
||||
.expected_utf_data = "\xF0\x9F\x92\xA9",
|
||||
},
|
||||
};
|
||||
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
const bool is_overflow_test = (j == 1);
|
||||
for (int i = 0; i < ARRAY_LENGTH(cases); ++i) {
|
||||
printf("Case %i (is_overflow_test=%u): %s\n", i, is_overflow_test, cases[i].script);
|
||||
|
||||
EXECUTE_SCRIPT(cases[i].script);
|
||||
const jerry_value_t s = JS_GLOBAL_GET_VALUE("str");
|
||||
|
||||
const jerry_size_t utf8_size = jerry_get_utf8_string_size(s);
|
||||
// U+1F4A9 is expected to get encoded into 4 bytes of UTF-8:
|
||||
cl_assert_equal_i(utf8_size, cases[i].expected_utf_size);
|
||||
|
||||
const size_t buffer_size = utf8_size ? (is_overflow_test ? (utf8_size - 1) : utf8_size) : 0;
|
||||
|
||||
// malloc, so DUMA will detect buffer overflows:
|
||||
jerry_char_t *utf8_buffer = malloc(buffer_size);
|
||||
|
||||
const jerry_size_t copied_size =
|
||||
jerry_string_to_utf8_char_buffer(s, utf8_buffer, buffer_size);
|
||||
if (!is_overflow_test) {
|
||||
cl_assert_equal_i(copied_size, cases[i].expected_utf_size);
|
||||
if (cases[i].expected_utf_size) {
|
||||
cl_assert_equal_m(utf8_buffer, cases[i].expected_utf_data, cases[i].expected_utf_size);
|
||||
}
|
||||
} else {
|
||||
// When buffer is too small, expect 0 bytes copied:
|
||||
cl_assert_equal_i(copied_size, 0);
|
||||
}
|
||||
jerry_release_value(s);
|
||||
|
||||
free(utf8_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_rocky_text_encoding__jerry_utf8_to_cesu8_conversion(void) {
|
||||
struct {
|
||||
const char *const utf8_input;
|
||||
const char *const cesu8_output;
|
||||
} cases[] = {
|
||||
{
|
||||
.utf8_input = "",
|
||||
.cesu8_output = "",
|
||||
},
|
||||
{
|
||||
.utf8_input = "abc",
|
||||
.cesu8_output = "abc",
|
||||
},
|
||||
{
|
||||
// U+1F4A9 expands to surrogate pair:
|
||||
.utf8_input = "abc\xF0\x9F\x92\xA9xyz",
|
||||
.cesu8_output = "abc\xed\xa0\xbd\xed\xb2\xa9xyz",
|
||||
},
|
||||
{
|
||||
// Be lax with surrogates: even though they're not supposed to appear in UTF-8,
|
||||
// just copy them over to the CESU-8 output, even a "half pair":
|
||||
.utf8_input = "\xed\xa0\xbd",
|
||||
.cesu8_output = "\xed\xa0\xbd",
|
||||
},
|
||||
};
|
||||
for (int i = 0; i < ARRAY_LENGTH(cases); ++i) {
|
||||
jerry_char_t output[32] = {};
|
||||
const jerry_value_t s = jerry_create_string_utf8((const jerry_char_t *)cases[i].utf8_input);
|
||||
const jerry_size_t copied_bytes = jerry_string_to_char_buffer(s, output, sizeof(output));
|
||||
cl_assert_equal_i(copied_bytes, strlen(cases[i].cesu8_output));
|
||||
if (copied_bytes) {
|
||||
cl_assert_equal_m(output, cases[i].cesu8_output, copied_bytes);
|
||||
}
|
||||
// TODO: test equality/hash
|
||||
jerry_release_value(s);
|
||||
}
|
||||
}
|
231
tests/fw/javascript/wscript
Normal file
231
tests/fw/javascript/wscript
Normal file
|
@ -0,0 +1,231 @@
|
|||
import os
|
||||
|
||||
from waftools.pebble_test import clar
|
||||
import sh
|
||||
|
||||
|
||||
def rocky_clar(ctx, **kwargs):
|
||||
if ctx.variant == 'test_rocky_emx':
|
||||
kwargs["sources_ant_glob"] += " applib-targets/emscripten/emscripten_jerry_port.c"
|
||||
jerry_uses = ['emscripten_jerry_api', 'jerry_port_includes']
|
||||
else:
|
||||
kwargs["sources_ant_glob"] += " src/fw/applib/rockyjs/jerry_port.c"
|
||||
jerry_uses = ['jerry_port_includes', 'jerry_core', 'jerry_libm']
|
||||
|
||||
kwargs["use"] = jerry_uses + (kwargs["use"] if "use" in kwargs else [])
|
||||
jerry_defines = ['CAPABILITY_HAS_JAVASCRIPT=1', 'CAPABILITY_JAVASCRIPT_BYTECODE_VERSION=1',
|
||||
'JMEM_STATS=1']
|
||||
kwargs["defines"] = jerry_defines + kwargs.get("defines", [])
|
||||
kwargs["defines"].extend(ctx.env.test_image_defines)
|
||||
clar(ctx, **kwargs)
|
||||
|
||||
|
||||
def build(ctx):
|
||||
if ctx.env.NOJS:
|
||||
return
|
||||
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob =
|
||||
" tests/fakes/fake_applib_resource.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_global.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c",
|
||||
test_sources_ant_glob = "test_rocky_api_global.c")
|
||||
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob =
|
||||
" tests/fakes/fake_applib_resource.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_global.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_graphics.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_graphics_color.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_graphics_text.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util_args.c",
|
||||
test_sources_ant_glob = "test_rocky_api_graphics.c")
|
||||
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob =
|
||||
" tests/fakes/fake_applib_resource.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_global.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_graphics.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_graphics_color.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util_args.c",
|
||||
test_sources_ant_glob = "test_rocky_api_graphics_color.c")
|
||||
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob =
|
||||
" tests/fakes/fake_applib_resource.c"
|
||||
" src/fw/applib/graphics/gtypes.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_global.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_graphics.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_graphics_path2d.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util_args.c",
|
||||
test_sources_ant_glob = "test_rocky_api_graphics_path2d.c")
|
||||
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob =
|
||||
" tests/fakes/fake_applib_resource.c"
|
||||
" src/fw/applib/graphics/gpath.c"
|
||||
" src/fw/applib/graphics/graphics_circle.c"
|
||||
" src/fw/applib/graphics/graphics.c"
|
||||
" src/fw/applib/graphics/bitblt.c"
|
||||
" src/fw/applib/graphics/8_bit/bitblt_private.c"
|
||||
" src/fw/applib/graphics/8_bit/framebuffer.c"
|
||||
" src/fw/applib/graphics/framebuffer.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw.c"
|
||||
" src/fw/applib/graphics/graphics_private_raw_mask.c"
|
||||
" src/fw/applib/graphics/gbitmap.c"
|
||||
" src/fw/applib/graphics/gtypes.c"
|
||||
" src/fw/applib/graphics/gcolor_definitions.c"
|
||||
" src/fw/applib/graphics/graphics_line.c"
|
||||
" src/fw/applib/graphics/graphics_private.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_global.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_graphics.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_graphics_color.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_graphics_path2d.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util_args.c",
|
||||
test_sources_ant_glob = "test_rocky_api_graphics_rendering.c"
|
||||
)
|
||||
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob =
|
||||
" tests/fakes/fake_applib_resource.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_global.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_tickservice.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c",
|
||||
test_sources_ant_glob = "test_rocky_api_tickservice.c")
|
||||
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/rockyjs/api/rocky_api_global.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_preferences.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c",
|
||||
test_sources_ant_glob = "test_rocky_api_preferences.c")
|
||||
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/util/dict.c"
|
||||
" tests/fakes/fake_events.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_global.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_app_message.c",
|
||||
test_sources_ant_glob = "test_rocky_api_app_message.c")
|
||||
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/util/dict.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_global.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_watchinfo.c",
|
||||
test_sources_ant_glob = "test_rocky_api_watchinfo.c")
|
||||
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_global.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c",
|
||||
test_sources_ant_glob = "test_rocky_api_util.c")
|
||||
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_graphics_color.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util_args.c",
|
||||
test_sources_ant_glob = "test_rocky_api_util_args.c")
|
||||
|
||||
# Tests that should be skipped when running unit tests with the fake
|
||||
# JerryScript (emscripten_jerry_api.c):
|
||||
if ctx.variant != 'test_rocky_emx':
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob =
|
||||
" tests/fakes/fake_applib_resource.c" \
|
||||
" tests/fakes/fake_clock.c" \
|
||||
" src/fw/applib/graphics/gtypes.c" \
|
||||
" src/fw/applib/rockyjs/rocky.c" \
|
||||
" src/fw/applib/rockyjs/api/rocky_api.c" \
|
||||
" src/fw/applib/rockyjs/api/rocky_api_datetime.c" \
|
||||
" src/fw/applib/rockyjs/api/rocky_api_global.c" \
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c" \
|
||||
" src/fw/applib/rockyjs/api/rocky_api_tickservice.c" \
|
||||
" src/fw/applib/rockyjs/api/rocky_api_timers.c" \
|
||||
" src/fw/applib/rockyjs/api/rocky_api_graphics.c" \
|
||||
" src/fw/applib/rockyjs/api/rocky_api_graphics_path2d.c" \
|
||||
" src/fw/applib/rockyjs/api/rocky_api_graphics_color.c" \
|
||||
" src/fw/applib/rockyjs/api/rocky_api_graphics_text.c" \
|
||||
" src/fw/applib/rockyjs/api/rocky_api_memory.c" \
|
||||
" src/fw/applib/rockyjs/api/rocky_api_console.c" \
|
||||
" src/fw/applib/rockyjs/api/rocky_api_preferences.c" \
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c" \
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util_args.c",
|
||||
test_sources_ant_glob = "test_js.c")
|
||||
|
||||
# No snapshot support in emscripten_jerry_api.c :D
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob =
|
||||
" tests/fakes/fake_applib_resource.c"
|
||||
" src/fw/applib/rockyjs/rocky.c"
|
||||
" src/fw/applib/rockyjs/rocky_res.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c",
|
||||
test_sources_ant_glob = "test_rocky_res.c")
|
||||
|
||||
# When building unit tests with emscripten, skip this one because we're
|
||||
# using the console.log/warn/error implementations of the browser/node.
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob =
|
||||
" tests/fakes/fake_applib_resource.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_global.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_console.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c",
|
||||
test_sources_ant_glob = "test_rocky_api_console.c")
|
||||
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob =
|
||||
" tests/fakes/fake_applib_resource.c"
|
||||
" tests/fakes/fake_clock.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_global.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_datetime.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c",
|
||||
test_sources_ant_glob = "test_rocky_api_datetime.c",
|
||||
defines=['CAPABILITY_HAS_JAVASCRIPT=1',
|
||||
'CAPABILITY_JAVASCRIPT_BYTECODE_VERSION=1'])
|
||||
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob=" tests/fakes/fake_applib_resource.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_global.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_memory.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c",
|
||||
test_sources_ant_glob = "test_rocky_api_memory.c")
|
||||
|
||||
# Currently we're leaving the browser/node's setTimeout + friends alone
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob =
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_timers.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c",
|
||||
test_sources_ant_glob = "test_rocky_api_timers.c")
|
||||
|
||||
# Emscripten transpiled jerry-api uses UTF8 internally.
|
||||
rocky_clar(ctx,
|
||||
sources_ant_glob=
|
||||
" src/fw/applib/rockyjs/api/rocky_api_errors.c"
|
||||
" src/fw/applib/rockyjs/api/rocky_api_util.c",
|
||||
test_sources_ant_glob = "test_rocky_text_encoding.c")
|
||||
|
||||
|
||||
# vim:filetype=python
|
Loading…
Add table
Add a link
Reference in a new issue