mirror of
https://github.com/google/pebble.git
synced 2025-03-24 20:49:05 +00:00
620 lines
17 KiB
C
620 lines
17 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 "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;
|
|
}
|
|
}
|