Skip to content

Commit 8418503

Browse files
authored
Add test to weak-node-api (#321)
* Add scaffold for testing * Implemented two simple tests * Add test job in the check workflow * Using the NAPI_NO_RETURN definition instead of __attribute__((noreturn)) * Using unreachable instead of noreturn * Use C++20
1 parent 9a5f8e6 commit 8418503

File tree

6 files changed

+119
-19
lines changed

6 files changed

+119
-19
lines changed

.github/workflows/check.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,32 @@ jobs:
7676
- run: npm ci
7777
- run: npm run bootstrap
7878
- run: npm test
79+
weak-node-api-tests:
80+
if: github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'weak-node-api')
81+
strategy:
82+
fail-fast: false
83+
matrix:
84+
runner:
85+
- ubuntu-latest
86+
- windows-latest
87+
- macos-latest
88+
runs-on: ${{ matrix.runner }}
89+
name: Weak Node-API tests (${{ matrix.runner }})
90+
steps:
91+
- uses: actions/checkout@v4
92+
- uses: actions/setup-node@v4
93+
with:
94+
node-version: lts/jod
95+
- run: npm ci
96+
- run: npm run build
97+
- name: Prepare weak-node-api
98+
run: npm run prepare-weak-node-api --workspace weak-node-api
99+
- name: Build and run weak-node-api C++ tests
100+
run: |
101+
cmake -S . -B build -DBUILD_TESTS=ON
102+
cmake --build build
103+
ctest --test-dir build --output-on-failure
104+
working-directory: packages/weak-node-api
79105
test-ios:
80106
if: github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'Apple 🍎')
81107
name: Test app (iOS)

packages/weak-node-api/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@
99

1010
# Copied from node-api-headers by scripts/copy-node-api-headers.ts
1111
/include/
12+
13+
# Clang cache
14+
/.cache/

packages/weak-node-api/CMakeLists.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,17 @@ if(APPLE)
3838
)
3939
endif()
4040

41-
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17)
41+
# C++20 is needed to use designated initializers
42+
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
4243
target_compile_definitions(${PROJECT_NAME} PRIVATE NAPI_VERSION=8)
4344

4445
target_compile_options(${PROJECT_NAME} PRIVATE
4546
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
4647
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Werror>
4748
)
49+
50+
option(BUILD_TESTS "Build the tests" OFF)
51+
if(BUILD_TESTS)
52+
enable_testing()
53+
add_subdirectory(tests)
54+
endif()

packages/weak-node-api/scripts/generate-weak-node-api.ts

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,24 @@ export function generateHeader(functions: FunctionDecl[]) {
1818
"#include <node_api.h>", // Node-API
1919
"#include <stdio.h>", // fprintf()
2020
"#include <stdlib.h>", // abort()
21+
"",
22+
// Ideally we would have just used NAPI_NO_RETURN, but
23+
// __declspec(noreturn) (when building with Microsoft Visual C++) cannot be used on members of a struct
24+
// TODO: If we targeted C++23 we could use std::unreachable()
25+
"#if defined(__GNUC__)",
26+
"#define WEAK_NODE_API_UNREACHABLE __builtin_unreachable();",
27+
"#else",
28+
"#define WEAK_NODE_API_UNREACHABLE __assume(0);",
29+
"#endif",
30+
"",
2131
// Generate the struct of function pointers
2232
"struct WeakNodeApiHost {",
23-
...functions.map(
24-
({ returnType, noReturn, name, argumentTypes }) =>
25-
`${returnType} ${
26-
noReturn ? " __attribute__((noreturn))" : ""
27-
}(*${name})(${argumentTypes.join(", ")});`,
33+
...functions.map(({ returnType, name, argumentTypes }) =>
34+
[
35+
returnType,
36+
// Signature
37+
`(*${name})(${argumentTypes.join(", ")});`,
38+
].join(" "),
2839
),
2940
"};",
3041
"typedef void(*InjectHostFunction)(const WeakNodeApiHost&);",
@@ -46,25 +57,26 @@ export function generateSource(functions: FunctionDecl[]) {
4657
"};",
4758
``,
4859
// Generate function calling into the host
49-
...functions.flatMap(({ returnType, noReturn, name, argumentTypes }) => {
60+
...functions.flatMap(({ returnType, name, argumentTypes, noReturn }) => {
5061
return [
51-
`extern "C" ${returnType} ${
52-
noReturn ? " __attribute__((noreturn))" : ""
53-
}${name}(${argumentTypes
54-
.map((type, index) => `${type} arg${index}`)
55-
.join(", ")}) {`,
62+
'extern "C"',
63+
returnType,
64+
name,
65+
"(",
66+
argumentTypes.map((type, index) => `${type} arg${index}`).join(", "),
67+
") {",
5668
`if (g_host.${name} == nullptr) {`,
5769
` fprintf(stderr, "Node-API function '${name}' called before it was injected!\\n");`,
5870
" abort();",
5971
"}",
60-
(returnType === "void" ? "" : "return ") +
61-
"g_host." +
62-
name +
63-
"(" +
64-
argumentTypes.map((_, index) => `arg${index}`).join(", ") +
65-
");",
72+
returnType === "void" ? "" : "return ",
73+
`g_host.${name}`,
74+
"(",
75+
argumentTypes.map((_, index) => `arg${index}`).join(", "),
76+
");",
77+
noReturn ? "WEAK_NODE_API_UNREACHABLE" : "",
6678
"};",
67-
];
79+
].join(" ");
6880
}),
6981
].join("\n");
7082
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Include(FetchContent)
2+
3+
FetchContent_Declare(
4+
Catch2
5+
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
6+
GIT_TAG v3.11.0
7+
)
8+
9+
FetchContent_MakeAvailable(Catch2)
10+
11+
add_executable(weak-node-api-tests
12+
test_inject.cpp
13+
)
14+
target_link_libraries(weak-node-api-tests
15+
PRIVATE
16+
weak-node-api
17+
Catch2::Catch2WithMain
18+
)
19+
20+
target_compile_features(weak-node-api-tests PRIVATE cxx_std_20)
21+
target_compile_definitions(weak-node-api-tests PRIVATE NAPI_VERSION=8)
22+
23+
# As per https://github.com/catchorg/Catch2/blob/devel/docs/cmake-integration.md#catchcmake-and-catchaddtestscmake
24+
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
25+
include(CTest)
26+
include(Catch)
27+
catch_discover_tests(weak-node-api-tests)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include <catch2/catch_test_macros.hpp>
2+
#include <weak_node_api.hpp>
3+
4+
TEST_CASE("inject_weak_node_api_host") {
5+
SECTION("is callable") {
6+
WeakNodeApiHost host{};
7+
inject_weak_node_api_host(host);
8+
}
9+
10+
SECTION("propagates calls to napi_create_object") {
11+
static bool called = false;
12+
auto my_create_object = [](napi_env env,
13+
napi_value *result) -> napi_status {
14+
called = true;
15+
return napi_status::napi_ok;
16+
};
17+
WeakNodeApiHost host{.napi_create_object = my_create_object};
18+
inject_weak_node_api_host(host);
19+
20+
napi_value result;
21+
napi_create_object({}, &result);
22+
23+
REQUIRE(called);
24+
}
25+
}

0 commit comments

Comments
 (0)