Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: integration tests, builtin tests, backtracking #35

Merged
merged 3 commits into from
Apr 25, 2024
Merged
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
6 changes: 3 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ jobs:
- name: Build StarlingMonkey
run: |
cmake -S . -B cmake-build-debug -DCMAKE_BUILD_TYPE=Debug
cmake --build cmake-build-debug --parallel 4
cmake --build cmake-build-debug --parallel 4 --target all integration-test-server

- name: StarlingMonkey Integration Tests
- name: StarlingMonkey E2E & Integration Tests
run: |
CTEST_OUTPUT_ON_FAILURE=1 make -C cmake-build-debug test
CTEST_OUTPUT_ON_FAILURE=1 ctest --test-dir cmake-build-debug -j4
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@
# Rust compilation output
/target

/tests/cases/*/*.wasm
/tests/cases/*/*.log
/tests/e2e/*/*.wasm
/tests/e2e/*/*.log
/tests/integration/*/*.wasm
/tests/integration/*/*.log
18 changes: 4 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,28 +42,18 @@ cmake --build cmake-build-release --parallel 8

4. Testing the build

Integration tests require [`Wasmtime`](https://wasmtime.dev/) to be installed (`cargo install wasmtime-cli`).

Before tests can be run, the cases must first be built, and then they can be tested:

```bash
CTEST_OUTPUT_ON_FAILURE=1 make -C cmake-build-debug test
```

Individual tests can also be run from within the build folder using `ctest` directly:
After completing the build (a debug build in this case), the integration test runner can be built:

```bash
ctest --test-dir cmake-build-debug -R smoke
cmake --build cmake-build-debug --target integration-test-server
```

Or, to directly run the tests on Wasmtime, use `wasmtime serve` via:
Then tests can be run with `ctest` directly via:

```bash
wasmtime serve -S common tests/cases/smoke/smoke.wasm
CTEST_OUTPUT_ON_FAILURE=1 ctest --test-dir cmake-build-debug -j8
```

Then visit http://0.0.0.0:8080/

5. Using the runtime with other JS applications

The build directory contains a shell script `componentize.sh` that can be used to create components from JS applications. `componentize.sh` takes a single argument, the path to the JS application, and creates a component with a name of the form `[input-file-name].wasm` in the current working directory.
Expand Down
64 changes: 53 additions & 11 deletions runtime/script_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,20 +71,62 @@ static const char* resolve_extension(const char* resolved_path) {

static const char* resolve_path(const char* path, const char* base, size_t base_len) {
MOZ_ASSERT(base);
if (path[0] == '/') {
return resolve_extension(strdup(path));
}
if (path[0] == '.' && path[1] == '/') {
path = path + 2;
}
while (base_len > 0 && base[base_len - 1] != '/') {
base_len--;
}
size_t len = base_len + strlen(path) + 1;
char* resolved_path = new char[len];
strncpy(resolved_path, base, base_len);
strncpy(resolved_path + base_len, path, len - base_len);
MOZ_ASSERT(strlen(resolved_path) == len - 1);
size_t path_len = strlen(path);

// create the maximum buffer size as a working buffer
char* resolved_path = new char[base_len + path_len + 1];

// copy the base in if used
size_t resolved_len = base_len;
if (path[0] == '/') {
resolved_len = 0;
} else {
strncpy(resolved_path, base, base_len);
}

// Iterate through each segment of the path, copying each segment into the resolved path,
// while handling backtracking for .. segments and skipping . segments appropriately.
size_t path_from_idx = 0;
size_t path_cur_idx = 0;
while (path_cur_idx < path_len) {
// read until the end or the next / to get the segment position
// as the substring between path_from_idx and path_cur_idx
while (path_cur_idx < path_len && path[path_cur_idx] != '/')
path_cur_idx++;
if (path_cur_idx == path_from_idx)
break;
// . segment to skip
if (path_cur_idx - path_from_idx == 1 && path[path_from_idx] == '.') {
path_cur_idx++;
path_from_idx = path_cur_idx;
continue;
}
// .. segment backtracking
if (path_cur_idx - path_from_idx == 2 && path[path_from_idx] == '.' && path[path_from_idx + 1] == '.') {
path_cur_idx++;
path_from_idx = path_cur_idx;
if (resolved_len > 0 && resolved_path[resolved_len - 1] == '/') {
resolved_len --;
}
while (resolved_len > 0 && resolved_path[resolved_len - 1] != '/') {
resolved_len--;
}
continue;
}
// normal segment to copy (with the trailing / if not the last segment)
if (path[path_cur_idx] == '/')
path_cur_idx++;
strncpy(resolved_path + resolved_len, path + path_from_idx, path_cur_idx - path_from_idx);
resolved_len += path_cur_idx - path_from_idx;
path_from_idx = path_cur_idx;
}

// finalize the buffer
resolved_path[resolved_len] = '\0';
MOZ_ASSERT(strlen(resolved_path) == resolved_len);
return resolve_extension(resolved_path);
}

Expand Down
2 changes: 0 additions & 2 deletions tests/cases/smoke/expect_serve_stdout.txt

This file was deleted.

2 changes: 2 additions & 0 deletions tests/e2e/smoke/expect_serve_stdout.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
stdout [0] :: Log: chaining to /chained
stdout [1] :: Log: post resp [object Response]
2 changes: 1 addition & 1 deletion tests/cases/smoke/smoke.js → tests/e2e/smoke/smoke.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ async function main(event) {

let url = new URL(event.request.url);
if (url.pathname === "/") {
console.log(`chaining from ${url} to /chained`);
console.log(`chaining to /chained`);
let response = await fetch("/chained");
let body = await response.text();
resolve(new Response(body));
Expand Down
File renamed without changes.
File renamed without changes.
39 changes: 39 additions & 0 deletions tests/integration/assert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export class AssertionError extends Error {
constructor (msg) {
super(msg);
}
}

function fail (check, message, actual, expected) {
let errText = check;
if (message)
errText += ` - ${message}`;
if (actual || expected)
errText += `\n\nGot:\n\n${JSON.stringify(actual)}\n\nExpected:\n\n${JSON.stringify(expected)}`
throw new AssertionError(errText);
}

export function strictEqual (actual, expected, message) {
if (actual !== expected) {
fail('not strict equal', message, actual, expected);
}
}

export function throws(func, errorClass, errorMessage) {
try {
func();
} catch (err) {
if (errorClass) {
if (!(err instanceof errorClass)) {
return fail(`not expected error instance calling \`${func.toString()}\``, errorMessage, err.name, errorClass.name);
}
}
if (errorMessage) {
if (err.message !== errorMessage) {
return fail(`not expected error message calling \`${func.toString()}\``, errorMessage, err.message, errorMessage);
}
}
return;
}
fail(`Expected \`${func.toString()}\` to throw, but it didn't`);
}
Loading
Loading