Skip to content

Commit 6824da2

Browse files
committed
always build the napi tests
1 parent 27ad000 commit 6824da2

12 files changed

Lines changed: 762 additions & 320 deletions

File tree

CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,21 @@ endif()
100100
FetchContent_MakeAvailable_With_Message(arcana.cpp)
101101
set_property(TARGET arcana PROPERTY FOLDER Dependencies)
102102

103+
if(ANDROID)
104+
FetchContent_GetProperties(AndroidExtensions)
105+
if(NOT AndroidExtensions_POPULATED)
106+
FetchContent_Populate(AndroidExtensions)
107+
FetchContent_GetProperties(AndroidExtensions)
108+
message(STATUS "Patching AndroidExtensions Globals.cpp in ${androidextensions_SOURCE_DIR}")
109+
file(COPY
110+
${CMAKE_CURRENT_SOURCE_DIR}/patches/AndroidExtensions/Globals.cpp
111+
DESTINATION ${androidextensions_SOURCE_DIR}/Source)
112+
add_subdirectory(${androidextensions_SOURCE_DIR} ${androidextensions_BINARY_DIR})
113+
else()
114+
add_subdirectory(${androidextensions_SOURCE_DIR} ${androidextensions_BINARY_DIR})
115+
endif()
116+
endif()
117+
103118
if(JSRUNTIMEHOST_POLYFILL_XMLHTTPREQUEST)
104119
FetchContent_MakeAvailable_With_Message(UrlLib)
105120
set_property(TARGET UrlLib PROPERTY FOLDER Dependencies)

Core/Node-API/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ elseif(ANDROID)
77
set(NAPI_JAVASCRIPT_ENGINE "V8" CACHE STRING "JavaScript engine for Node-API")
88
elseif(UNIX)
99
set(NAPI_JAVASCRIPT_ENGINE "JavaScriptCore" CACHE STRING "JavaScript engine for Node-API")
10-
set(JAVASCRIPTCORE_LIBRARY "/usr/lib/x86_64-linux-gnu/libjavascriptcoregtk-4.1.so" CACHE STRING "Path to the JavaScriptCore shared library")
10+
find_library(JAVASCRIPTCORE_LIBRARY javascriptcoregtk-4.1)
11+
if(NOT JAVASCRIPTCORE_LIBRARY)
12+
message(FATAL_ERROR "JavaScriptCore library not found. Please install libwebkit2gtk-4.1-dev")
13+
endif()
1114
else()
1215
message(FATAL_ERROR "Unable to select Node-API JavaScript engine for platform")
1316
endif()

Tests/NodeApi/CMakeLists.txt

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
set(NODE_API_TEST_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
22

3-
option(JSR_NODE_API_BUILD_NATIVE_TESTS "Build Node-API native addon test modules" OFF)
3+
option(JSR_NODE_API_BUILD_NATIVE_TESTS "Build Node-API native addon test modules" ON)
44

5-
if(JSR_NODE_API_BUILD_NATIVE_TESTS)
6-
set(JSR_NODE_API_NATIVE_TEST_DIRS
7-
2_function_arguments
8-
3_callbacks
9-
4_object_factory
10-
5_function_factory
11-
)
12-
endif()
5+
set(JSR_NODE_API_NATIVE_TEST_DIRS
6+
2_function_arguments
7+
3_callbacks
8+
4_object_factory
9+
5_function_factory
10+
)
1311

1412
function(node_api_copy_test_sources TARGET_NAME)
1513
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
@@ -98,17 +96,26 @@ add_dependencies(NodeApiTests node_lite)
9896

9997
add_custom_target(NodeApiModules)
10098

99+
# Always define which tests are available, regardless of whether we build the native modules
100+
list(JOIN JSR_NODE_API_NATIVE_TEST_DIRS "," NODE_API_NATIVE_TESTS_STRING)
101+
target_compile_definitions(node_lite
102+
PRIVATE
103+
NODE_API_AVAILABLE_NATIVE_TESTS=\"${NODE_API_NATIVE_TESTS_STRING}\"
104+
)
105+
target_compile_definitions(NodeApiTests
106+
PRIVATE
107+
NODE_API_AVAILABLE_NATIVE_TESTS=\"${NODE_API_NATIVE_TESTS_STRING}\"
108+
)
109+
110+
# Only define NODE_API_TESTS_HAVE_NATIVE_MODULES when actually building native modules
101111
if(JSR_NODE_API_BUILD_NATIVE_TESTS)
102-
list(JOIN JSR_NODE_API_NATIVE_TEST_DIRS "," NODE_API_NATIVE_TESTS_STRING)
103112
target_compile_definitions(node_lite
104113
PRIVATE
105114
NODE_API_TESTS_HAVE_NATIVE_MODULES=1
106-
NODE_API_AVAILABLE_NATIVE_TESTS=\"${NODE_API_NATIVE_TESTS_STRING}\"
107115
)
108116
target_compile_definitions(NodeApiTests
109117
PRIVATE
110118
NODE_API_TESTS_HAVE_NATIVE_MODULES=1
111-
NODE_API_AVAILABLE_NATIVE_TESTS=\"${NODE_API_NATIVE_TESTS_STRING}\"
112119
)
113120
endif()
114121
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include "child_process.h"
2+
3+
namespace node_api_tests {
4+
5+
ProcessResult SpawnSync(std::string_view /*command*/, std::vector<std::string> /*args*/)
6+
{
7+
ProcessResult result{};
8+
result.status = -1;
9+
result.std_error = "child_process.spawnSync is not supported on this platform.";
10+
result.std_output.clear();
11+
return result;
12+
}
13+
14+
} // namespace node_api_tests
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
#include "child_process.h"
2+
3+
#include <cerrno>
4+
#include <cstdlib>
5+
#include <fcntl.h>
6+
#include <sys/wait.h>
7+
#include <unistd.h>
8+
#include <cassert>
9+
#include <cstdio>
10+
#include <cstring>
11+
#include <string>
12+
#include <vector>
13+
14+
#if defined(__ANDROID__)
15+
#include <android/api-level.h>
16+
#endif
17+
18+
#if !defined(__ANDROID__)
19+
#include <spawn.h>
20+
#elif (__ANDROID_API__ >= 29)
21+
#include <spawn.h>
22+
#endif
23+
24+
#ifndef VerifyElseExit
25+
#define VerifyElseExit(condition) \
26+
do { \
27+
if (!(condition)) { \
28+
ExitOnError(#condition, nullptr); \
29+
} \
30+
} while (false)
31+
#endif
32+
33+
#ifndef VerifyElseExitWithCleanup
34+
#define VerifyElseExitWithCleanup(condition, actions_ptr) \
35+
do { \
36+
if (!(condition)) { \
37+
ExitOnError(#condition, actions_ptr); \
38+
} \
39+
} while (false)
40+
#endif
41+
42+
#if defined(__ANDROID__) && (__ANDROID_API__ < 29)
43+
44+
namespace node_api_tests {
45+
46+
ProcessResult SpawnSync(std::string_view /*command*/,
47+
std::vector<std::string> /*args*/) {
48+
ProcessResult result{};
49+
result.status = -1;
50+
result.std_error = "child_process.spawnSync is not supported on this platform.";
51+
result.std_output.clear();
52+
return result;
53+
}
54+
55+
} // namespace node_api_tests
56+
57+
#else
58+
59+
extern char** environ;
60+
61+
namespace node_api_tests {
62+
63+
namespace {
64+
65+
std::string ReadFromFd(int fd);
66+
void ExitOnError(const char* message, posix_spawn_file_actions_t* actions);
67+
68+
} // namespace
69+
70+
ProcessResult SpawnSync(std::string_view command,
71+
std::vector<std::string> args) {
72+
ProcessResult result{};
73+
74+
// These int arrays each comprise two file descriptors: { readEnd, writeEnd }.
75+
int stdout_pipe[2], stderr_pipe[2];
76+
VerifyElseExit(pipe(stdout_pipe) == 0);
77+
VerifyElseExit(pipe(stderr_pipe) == 0);
78+
79+
posix_spawn_file_actions_t actions;
80+
VerifyElseExit(posix_spawn_file_actions_init(&actions) == 0);
81+
82+
VerifyElseExitWithCleanup(posix_spawn_file_actions_adddup2(
83+
&actions, stdout_pipe[1], STDOUT_FILENO) == 0,
84+
&actions);
85+
VerifyElseExitWithCleanup(posix_spawn_file_actions_adddup2(
86+
&actions, stderr_pipe[1], STDERR_FILENO) == 0,
87+
&actions);
88+
89+
VerifyElseExitWithCleanup(
90+
posix_spawn_file_actions_addclose(&actions, stdout_pipe[0]) == 0,
91+
&actions);
92+
VerifyElseExitWithCleanup(
93+
posix_spawn_file_actions_addclose(&actions, stderr_pipe[0]) == 0,
94+
&actions);
95+
96+
std::vector<char*> argv;
97+
argv.push_back(strdup(std::string(command).c_str()));
98+
for (const std::string& arg : args) {
99+
argv.push_back(strdup(arg.c_str()));
100+
}
101+
argv.push_back(nullptr);
102+
103+
pid_t pid;
104+
VerifyElseExitWithCleanup(
105+
posix_spawnp(&pid, argv[0], &actions, nullptr, argv.data(), environ) == 0,
106+
&actions);
107+
108+
posix_spawn_file_actions_destroy(&actions);
109+
110+
// Close the write ends of the pipes.
111+
close(stdout_pipe[1]);
112+
close(stderr_pipe[1]);
113+
114+
int wait_status;
115+
pid_t waited_pid;
116+
do {
117+
waited_pid = waitpid(pid, &wait_status, 0);
118+
} while (waited_pid == -1 && errno == EINTR);
119+
120+
VerifyElseExit(waited_pid == pid);
121+
122+
if (WIFEXITED(wait_status)) {
123+
result.status = WEXITSTATUS(wait_status);
124+
} else if (WIFSIGNALED(wait_status)) {
125+
result.status = 128 + WTERMSIG(wait_status);
126+
} else {
127+
result.status = 1;
128+
}
129+
result.std_output = ReadFromFd(stdout_pipe[0]);
130+
result.std_error = ReadFromFd(stderr_pipe[0]);
131+
132+
// Close the read ends of the pipes.
133+
close(stdout_pipe[0]);
134+
close(stderr_pipe[0]);
135+
136+
for (char* arg : argv) {
137+
free(arg);
138+
}
139+
140+
return result;
141+
}
142+
143+
namespace {
144+
145+
std::string ReadFromFd(int fd) {
146+
std::string result;
147+
constexpr size_t bufferSize = 4096;
148+
char buffer[bufferSize];
149+
ssize_t bytesRead;
150+
while (true) {
151+
bytesRead = read(fd, buffer, bufferSize);
152+
if (bytesRead > 0) {
153+
result.append(buffer, bytesRead);
154+
continue;
155+
}
156+
157+
if (bytesRead == 0) {
158+
break;
159+
}
160+
161+
if (errno == EINTR) {
162+
continue;
163+
}
164+
165+
ExitOnError("read", nullptr);
166+
}
167+
return result;
168+
}
169+
170+
// Format a readable error message, print it to console, and exit from the
171+
// application.
172+
void ExitOnError(const char* message, posix_spawn_file_actions_t* actions) {
173+
int err = errno;
174+
const char* err_msg = strerror(err);
175+
176+
fprintf(stderr, "%s failed with error %d: %s\n", message, err, err_msg);
177+
178+
if (actions != nullptr) {
179+
posix_spawn_file_actions_destroy(actions);
180+
}
181+
182+
exit(1);
183+
}
184+
185+
} // namespace
186+
187+
} // namespace node_api_tests
188+
189+
#endif // __ANDROID__

Tests/NodeApi/main.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#include "test_main.h"
2+
3+
#include <gtest/gtest.h>
4+
5+
#include <filesystem>
6+
#include <sstream>
7+
#include <string>
8+
#include <unordered_set>
9+
10+
#include "child_process.h"
11+
12+
namespace fs = std::filesystem;
13+
14+
namespace {
15+
16+
fs::path ResolveNodeLitePath(const fs::path& exe_path) {
17+
fs::path nodeLitePath = exe_path;
18+
nodeLitePath.replace_filename("node_lite");
19+
#if defined(_WIN32)
20+
nodeLitePath += ".exe";
21+
#endif
22+
return nodeLitePath;
23+
}
24+
25+
fs::path ResolveTestsRoot(const fs::path& exe_path) {
26+
fs::path testRootPath = exe_path.parent_path();
27+
fs::path js_root = testRootPath / "test";
28+
if (!fs::exists(js_root)) {
29+
testRootPath = testRootPath.parent_path();
30+
js_root = testRootPath / "test";
31+
}
32+
return js_root;
33+
}
34+
35+
std::unordered_set<std::string> ParseEnabledNativeSuites() {
36+
std::unordered_set<std::string> suites;
37+
#ifdef NODE_API_AVAILABLE_NATIVE_TESTS
38+
std::stringstream stream(NODE_API_AVAILABLE_NATIVE_TESTS);
39+
std::string entry;
40+
while (std::getline(stream, entry, ',')) {
41+
if (!entry.empty()) {
42+
suites.insert(entry);
43+
}
44+
}
45+
#endif
46+
return suites;
47+
}
48+
49+
} // namespace
50+
51+
int main(int argc, char** argv) {
52+
fs::path exe_path = fs::canonical(argv[0]);
53+
fs::path js_root = ResolveTestsRoot(exe_path);
54+
if (!fs::exists(js_root)) {
55+
std::cerr << "Error: Cannot find Node-API test directory." << std::endl;
56+
return EXIT_FAILURE;
57+
}
58+
59+
fs::path node_lite_path = ResolveNodeLitePath(exe_path);
60+
if (!fs::exists(node_lite_path)) {
61+
std::cerr << "Error: Cannot find node_lite executable at "
62+
<< node_lite_path << std::endl;
63+
return EXIT_FAILURE;
64+
}
65+
66+
node_api_tests::NodeApiTestConfig config{};
67+
config.js_root = js_root;
68+
config.run_script =
69+
[node_lite_path](const fs::path& script_path)
70+
-> node_api_tests::ProcessResult {
71+
return node_api_tests::SpawnSync(node_lite_path.string(),
72+
{script_path.string()});
73+
};
74+
config.enabled_native_suites = ParseEnabledNativeSuites();
75+
76+
node_api_tests::InitializeNodeApiTests(config);
77+
78+
::testing::InitGoogleTest(&argc, argv);
79+
node_api_tests::RegisterNodeApiTests();
80+
return RUN_ALL_TESTS();
81+
}

0 commit comments

Comments
 (0)