Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion PORTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Tests covering the engine-specific part of Node-API, defined in `js_native_api.h
| `test_reference` | Not ported | Medium |
| `test_reference_double_free` | Ported ✅ | Easy |
| `test_sharedarraybuffer` | Not ported | Medium |
| `test_string` | Not ported | Medium |
| `test_string` | Ported ✅ | Medium |
| `test_symbol` | Ported ✅ | Easy |
| `test_typedarray` | Not ported | Medium |

Expand Down
3 changes: 3 additions & 0 deletions tests/js-native-api/test_string/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
add_node_api_cts_addon(test_string test_string.c test_null.c)
# API like node_api_create_external_string_latin1 are available from NAPI_VERSION >= 10
target_compile_definitions(test_string PRIVATE NAPI_VERSION=10)
104 changes: 104 additions & 0 deletions tests/js-native-api/test_string/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
"use strict";

// Testing api calls for string
const test_string = loadAddon("test_string");
// The insufficient buffer test case allocates a buffer of size 4, including
// the null terminator.
const kInsufficientIdx = 3;

const asciiCases = [
"",
"hello world",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
"?!@#$%^&*()_+-=[]{}/.,<>'\"\\",
];

const latin1Cases = [
{
str: "¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿",
utf8Length: 62,
utf8InsufficientIdx: 1,
},
{
str: "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ",
utf8Length: 126,
utf8InsufficientIdx: 1,
},
];

const unicodeCases = [
{
str: "\u{2003}\u{2101}\u{2001}\u{202}\u{2011}",
utf8Length: 14,
utf8InsufficientIdx: 1,
},
];

function testLatin1Cases(str) {
assert.strictEqual(test_string.TestLatin1(str), str);
assert.strictEqual(test_string.TestLatin1AutoLength(str), str);
assert.strictEqual(test_string.TestLatin1External(str), str);
assert.strictEqual(test_string.TestLatin1ExternalAutoLength(str), str);
assert.strictEqual(test_string.TestPropertyKeyLatin1(str), str);
assert.strictEqual(test_string.TestPropertyKeyLatin1AutoLength(str), str);
assert.strictEqual(test_string.Latin1Length(str), str.length);

if (str !== "") {
assert.strictEqual(
test_string.TestLatin1Insufficient(str),
str.slice(0, kInsufficientIdx),
);
}
}

function testUnicodeCases(str, utf8Length, utf8InsufficientIdx) {
assert.strictEqual(test_string.TestUtf8(str), str);
assert.strictEqual(test_string.TestUtf16(str), str);
assert.strictEqual(test_string.TestUtf8AutoLength(str), str);
assert.strictEqual(test_string.TestUtf16AutoLength(str), str);
assert.strictEqual(test_string.TestUtf16External(str), str);
assert.strictEqual(test_string.TestUtf16ExternalAutoLength(str), str);
assert.strictEqual(test_string.TestPropertyKeyUtf8(str), str);
assert.strictEqual(test_string.TestPropertyKeyUtf8AutoLength(str), str);
assert.strictEqual(test_string.TestPropertyKeyUtf16(str), str);
assert.strictEqual(test_string.TestPropertyKeyUtf16AutoLength(str), str);
assert.strictEqual(test_string.Utf8Length(str), utf8Length);
assert.strictEqual(test_string.Utf16Length(str), str.length);

if (str !== "") {
assert.strictEqual(
test_string.TestUtf8Insufficient(str),
str.slice(0, utf8InsufficientIdx),
);
assert.strictEqual(
test_string.TestUtf16Insufficient(str),
str.slice(0, kInsufficientIdx),
);
}
}

asciiCases.forEach(testLatin1Cases);
asciiCases.forEach((str) =>
testUnicodeCases(str, str.length, kInsufficientIdx),
);
latin1Cases.forEach((it) => testLatin1Cases(it.str));
latin1Cases.forEach((it) =>
testUnicodeCases(it.str, it.utf8Length, it.utf8InsufficientIdx),
);
unicodeCases.forEach((it) =>
testUnicodeCases(it.str, it.utf8Length, it.utf8InsufficientIdx),
);

assert.throws(() => {
test_string.TestLargeUtf8();
}, /^Error: Invalid argument$/);

assert.throws(() => {
test_string.TestLargeLatin1();
}, /^Error: Invalid argument$/);

assert.throws(() => {
test_string.TestLargeUtf16();
}, /^Error: Invalid argument$/);

test_string.TestMemoryCorruption(" ".repeat(64 * 1024));
71 changes: 71 additions & 0 deletions tests/js-native-api/test_string/test_null.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include <js_native_api.h>

#include "../common.h"
#include "test_null.h"

#define DECLARE_TEST(charset, str_arg) \
static napi_value \
test_create_##charset(napi_env env, napi_callback_info info) { \
napi_value return_value, result; \
NODE_API_CALL(env, napi_create_object(env, &return_value)); \
\
add_returned_status(env, \
"envIsNull", \
return_value, \
"Invalid argument", \
napi_invalid_arg, \
napi_create_string_##charset(NULL, \
(str_arg), \
NAPI_AUTO_LENGTH, \
&result)); \
\
napi_create_string_##charset(env, NULL, NAPI_AUTO_LENGTH, &result); \
add_last_status(env, "stringIsNullNonZeroLength", return_value); \
\
napi_create_string_##charset(env, NULL, 0, &result); \
add_last_status(env, "stringIsNullZeroLength", return_value); \
\
napi_create_string_##charset(env, (str_arg), NAPI_AUTO_LENGTH, NULL); \
add_last_status(env, "resultIsNull", return_value); \
\
return return_value; \
}

static const char16_t something[] = {
(char16_t)'s',
(char16_t)'o',
(char16_t)'m',
(char16_t)'e',
(char16_t)'t',
(char16_t)'h',
(char16_t)'i',
(char16_t)'n',
(char16_t)'g',
(char16_t)'\0'
};

DECLARE_TEST(utf8, "something")
DECLARE_TEST(latin1, "something")
DECLARE_TEST(utf16, something)

void init_test_null(napi_env env, napi_value exports) {
napi_value test_null;

const napi_property_descriptor test_null_props[] = {
DECLARE_NODE_API_PROPERTY("test_create_utf8", test_create_utf8),
DECLARE_NODE_API_PROPERTY("test_create_latin1", test_create_latin1),
DECLARE_NODE_API_PROPERTY("test_create_utf16", test_create_utf16),
};

NODE_API_CALL_RETURN_VOID(env, napi_create_object(env, &test_null));
NODE_API_CALL_RETURN_VOID(env, napi_define_properties(
env, test_null, sizeof(test_null_props) / sizeof(*test_null_props),
test_null_props));

const napi_property_descriptor test_null_set = {
"testNull", NULL, NULL, NULL, NULL, test_null, napi_enumerable, NULL
};

NODE_API_CALL_RETURN_VOID(env,
napi_define_properties(env, exports, 1, &test_null_set));
}
8 changes: 8 additions & 0 deletions tests/js-native-api/test_string/test_null.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef TEST_JS_NATIVE_API_TEST_STRING_TEST_NULL_H_
#define TEST_JS_NATIVE_API_TEST_STRING_TEST_NULL_H_

#include <js_native_api.h>

void init_test_null(napi_env env, napi_value exports);

#endif // TEST_JS_NATIVE_API_TEST_STRING_TEST_NULL_H_
15 changes: 15 additions & 0 deletions tests/js-native-api/test_string/test_null.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"use strict";

// Test passing NULL to object-related Node-APIs.
const { testNull } = loadAddon("test_string");

const expectedResult = {
envIsNull: "Invalid argument",
stringIsNullNonZeroLength: "Invalid argument",
stringIsNullZeroLength: "napi_ok",
resultIsNull: "Invalid argument",
};

assert.deepStrictEqual(expectedResult, testNull.test_create_latin1());
assert.deepStrictEqual(expectedResult, testNull.test_create_utf8());
assert.deepStrictEqual(expectedResult, testNull.test_create_utf16());
Loading