Skip to content

Commit 19efaf3

Browse files
committed
wip, run tests under wasm
1 parent bafaf05 commit 19efaf3

3 files changed

Lines changed: 231 additions & 1 deletion

File tree

ci/scripts/r_wasm_test.cjs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
// Smoke-test and run the testthat suite for the arrow R package under webR.
19+
//
20+
// This script is called by r_wasm_test.sh after it sets up the CRAN-like
21+
// repo and installs the webr npm package.
22+
//
23+
// Environment variables:
24+
// ARROW_WASM_REPO_DIR - path to the local CRAN-like repo containing
25+
// the arrow wasm binary package
26+
27+
const { WebR } = require("webr");
28+
const http = require("http");
29+
const fs = require("fs");
30+
const path = require("path");
31+
32+
const repoDir = process.env.ARROW_WASM_REPO_DIR;
33+
if (!repoDir) {
34+
console.error("ERROR: ARROW_WASM_REPO_DIR not set");
35+
process.exit(1);
36+
}
37+
38+
async function main() {
39+
// Serve the local repo over HTTP so webR (Emscripten) can access it.
40+
// webR's R runs in an Emscripten sandbox and cannot access the host
41+
// filesystem directly — it fetches packages over HTTP instead.
42+
const server = http.createServer((req, res) => {
43+
const filePath = path.join(repoDir, decodeURIComponent(req.url));
44+
fs.readFile(filePath, (err, data) => {
45+
if (err) {
46+
res.writeHead(404);
47+
res.end();
48+
} else {
49+
res.writeHead(200);
50+
res.end(data);
51+
}
52+
});
53+
});
54+
server.listen(8080);
55+
console.log("✓ Repo server on :8080");
56+
57+
const webR = new WebR({
58+
RArgs: ["--quiet"],
59+
interactive: false,
60+
});
61+
62+
await webR.init();
63+
console.log("✓ webR initialized");
64+
65+
// Install the arrow Wasm package, put localhost:8080 before repo.r-wasm.org
66+
// (which is used for deps)
67+
await webR.installPackages(["arrow"], {
68+
repos: ["http://localhost:8080", "https://repo.r-wasm.org"],
69+
quiet: false,
70+
mount: false,
71+
});
72+
console.log("✓ arrow installed");
73+
74+
// Install test deps. TOOD: This is flakey. We could parse the DESCRIPTION
75+
// file to be more robust.
76+
await webR.installPackages(
77+
["testthat", "tibble", "dplyr", "withr", "pillar"],
78+
{
79+
repos: ["https://repo.r-wasm.org"],
80+
quiet: false,
81+
mount: false,
82+
},
83+
);
84+
console.log("✓ test dependencies installed");
85+
86+
// Test the package loads and functions basically
87+
const loadResult = await webR.evalRString(`
88+
library(arrow)
89+
cat("arrow loaded\\n")
90+
cat("R.version$os =", R.version$os, "\\n")
91+
use_threads <- getOption("arrow.use_threads")
92+
cat("arrow.use_threads =", use_threads, "\\n")
93+
stopifnot(identical(use_threads, FALSE))
94+
tab <- arrow::as_arrow_table(data.frame(x = 1:10, y = letters[1:10]))
95+
stopifnot(nrow(tab) == 10L)
96+
cat("Created Arrow table with", nrow(tab), "rows\\n")
97+
"PASS"
98+
`);
99+
100+
if (loadResult !== "PASS") {
101+
console.error("Package load test FAILED");
102+
await webR.close();
103+
server.close();
104+
process.exit(1);
105+
}
106+
console.log("✓ Package loads and works correctly");
107+
108+
// Run tests
109+
console.log("Running testthat suite under webR...");
110+
111+
const testResult = await webR.evalRString(`
112+
library(testthat)
113+
library(arrow)
114+
results <- testthat::test_package("arrow", reporter = "summary", stop_on_failure = FALSE)
115+
df <- as.data.frame(results)
116+
n_pass <- sum(df$passed)
117+
n_skip <- sum(df$skipped)
118+
n_fail <- sum(df$failed)
119+
n_error <- sum(df$error)
120+
cat(sprintf("Results: %d passed, %d skipped, %d failed, %d errors\\n",
121+
n_pass, n_skip, n_fail, n_error))
122+
if (n_fail > 0 || n_error > 0) "FAIL" else "PASS"
123+
`);
124+
125+
if (testResult !== "PASS") {
126+
console.error("testthat suite FAILED");
127+
await webR.close();
128+
server.close();
129+
process.exit(1);
130+
}
131+
console.log("✓ testthat suite passed");
132+
133+
console.log("✓ All tests passed!");
134+
await webR.close();
135+
server.close();
136+
}
137+
138+
main().catch((e) => {
139+
console.error("FAILED:", e);
140+
process.exit(1);
141+
});

ci/scripts/r_wasm_test.sh

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/env bash
2+
# Licensed to the Apache Software Foundation (ASF) under one
3+
# or more contributor license agreements. See the NOTICE file
4+
# distributed with this work for additional information
5+
# regarding copyright ownership. The ASF licenses this file
6+
# to you under the Apache License, Version 2.0 (the
7+
# "License"); you may not use this file except in compliance
8+
# with the License. You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing,
13+
# software distributed under the License is distributed on an
14+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
# KIND, either express or implied. See the License for the
16+
# specific language governing permissions and limitations
17+
# under the License.
18+
19+
# Test the arrow R package built for WebAssembly.
20+
#
21+
# This script is intended to run inside the ghcr.io/r-universe-org/build-wasm
22+
# Docker container after rwasm::build() has produced a .tgz binary. It:
23+
# 1. Sets up a CRAN-like repo structure from the built .tgz
24+
# 2. Installs the npm webr package (Node.js webR runtime)
25+
# 3. Boots webR, installs arrow from the local repo, and verifies:
26+
# - The package can be installed and loaded
27+
# - Multithreading is disabled (arrow.use_threads == FALSE)
28+
# - The testthat test suite runs
29+
#
30+
# Tests that require threading are automatically skipped via
31+
# skip_if_not(CanRunWithCapturedR()) since CanRunWithCapturedR() returns
32+
# FALSE under Emscripten.
33+
#
34+
# Usage:
35+
# r_wasm_test.sh <path-to-arrow-r-dir>
36+
#
37+
# Example:
38+
# r_wasm_test.sh /work
39+
#
40+
# The arrow .tgz file(s) should already exist in <path-to-arrow-r-dir>.
41+
42+
set -euxo pipefail
43+
44+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
45+
arrow_r_dir="${1:-.}"
46+
47+
# Set up a fake CRAN-like repo so we can install the package
48+
tgz_file=$(ls "${arrow_r_dir}"/arrow_*.tgz 2>/dev/null | head -1)
49+
if [ -z "${tgz_file}" ]; then
50+
echo "ERROR: No arrow_*.tgz found in ${arrow_r_dir}" >&2
51+
exit 1
52+
fi
53+
echo "Found Wasm binary: ${tgz_file}"
54+
55+
repo_dir=$(mktemp -d)
56+
# TODO: Not sure if we need this
57+
# Cover multiple R minor versions in case the npm webr package
58+
# uses a different R version than the Docker image's build R.
59+
for r_ver in 4.4 4.5 4.6; do
60+
contrib_dir="${repo_dir}/bin/emscripten/contrib/${r_ver}"
61+
mkdir -p "${contrib_dir}"
62+
cp "${tgz_file}" "${contrib_dir}/"
63+
# type=mac.binary matches .tgz file extension
64+
R -q -e "tools::write_PACKAGES('${contrib_dir}', type = 'mac.binary')"
65+
done
66+
67+
echo "Repo structure:"
68+
find "${repo_dir}" -type f
69+
70+
# Install webr in a temporary node project
71+
work_dir=$(mktemp -d)
72+
cd "${work_dir}"
73+
npm init -y > /dev/null 2>&1
74+
npm install --silent webr 2>/dev/null
75+
76+
# Run our test script
77+
ARROW_WASM_REPO_DIR="${repo_dir}" node "${SCRIPT_DIR}/r_wasm_test.cjs"
78+
79+
# Cleanup temp dirs
80+
rm -rf "${work_dir}" "${repo_dir}"

dev/tasks/r/github.linux.r-wasm.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
jobs:
2323
r-universe-wasm:
24-
name: "R-universe Wasm build"
24+
name: "R-universe Wasm build and test"
2525
runs-on: ubuntu-latest
2626
timeout-minutes: 60
2727

@@ -56,6 +56,15 @@ jobs:
5656
2>&1 | tee build-wasm.log
5757
'
5858
59+
- name: Smoke-test arrow in webR
60+
shell: bash
61+
run: |
62+
docker run --rm \
63+
-v "${PWD}/arrow:/arrow" \
64+
-w /tmp \
65+
ghcr.io/r-universe-org/build-wasm:latest \
66+
bash /arrow/ci/scripts/r_wasm_test.sh /arrow/r
67+
5968
- name: List generated artifacts
6069
if: always()
6170
shell: bash

0 commit comments

Comments
 (0)