mirror of
https://github.com/google/pebble.git
synced 2025-03-19 18:41:21 +00:00
589 lines
20 KiB
C
589 lines
20 KiB
C
|
/*
|
||
|
* 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);
|
||
|
}
|