Skip to content
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
45 changes: 40 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,25 @@ jobs:
with:
run_install: true

# Install emscripten for C++ module compilation tests.
- name: Install emscripten (Linux)
if: runner.os == 'Linux'
shell: bash
run: |
git clone https://github.com/emscripten-core/emsdk.git ~/emsdk
cd ~/emsdk
./emsdk install 4.0.21
./emsdk activate 4.0.21

- name: Install emscripten (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
git clone https://github.com/emscripten-core/emsdk.git $env:USERPROFILE\emsdk
cd $env:USERPROFILE\emsdk
.\emsdk install 4.0.21
.\emsdk activate 4.0.21

- name: Install psql (Windows)
if: runner.os == 'Windows'
run: choco install psql -y --no-progress
Expand Down Expand Up @@ -122,11 +141,27 @@ jobs:
- name: Install cargo-nextest
uses: taiki-e/install-action@nextest

- name: Run smoketests
# --test-threads=1 eliminates contention in the C# tests where they fight over bindings
# build artifacts.
# It also seemed to improve performance a fair amount (11m -> 6m)
run: cargo ci smoketests -- --test-threads=1
# --test-threads=1 eliminates contention in the C# tests where they fight over bindings
# build artifacts.
# It also seemed to improve performance a fair amount (11m -> 6m)
- name: Run smoketests (Linux)
if: runner.os == 'Linux'
shell: bash
run: |
if [ -f ~/emsdk/emsdk_env.sh ]; then
source ~/emsdk/emsdk_env.sh
fi
cargo ci smoketests -- --test-threads=1

# Due to Emscripten PATH issues this was separated to make sure OpenSSL still builds correctly
- name: Run smoketests (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
if (Test-Path "$env:USERPROFILE\emsdk\emsdk_env.ps1") {
& "$env:USERPROFILE\emsdk\emsdk_env.ps1" | Out-Null
}
cargo ci smoketests -- --test-threads=1

smoketests-python:
needs: [lints]
Expand Down
15 changes: 15 additions & 0 deletions crates/smoketests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,15 @@ macro_rules! require_pnpm {
};
}

#[macro_export]
macro_rules! require_emscripten {
() => {
if !$crate::have_emscripten() {
panic!("emcc (Emscripten) not found");
}
};
}

/// Helper macro for timing operations and printing results
macro_rules! timed {
($label:expr, $expr:expr) => {{
Expand Down Expand Up @@ -242,6 +251,12 @@ pub fn pnpm_path() -> Option<PathBuf> {
PNPM_PATH.get_or_init(|| which("pnpm").ok()).clone()
}

/// Returns true if Emscripten (emcc) is available on the system.
pub fn have_emscripten() -> bool {
static HAVE_EMSCRIPTEN: OnceLock<bool> = OnceLock::new();
*HAVE_EMSCRIPTEN.get_or_init(|| which("emcc").is_ok() || which("emcc.bat").is_ok())
}

/// A smoketest instance that manages a SpacetimeDB server and module project.
pub struct Smoketest {
/// The SpacetimeDB server guard (stops server on drop).
Expand Down
42 changes: 41 additions & 1 deletion crates/smoketests/tests/quickstart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use anyhow::{bail, Context, Result};
use regex::Regex;
use spacetimedb_smoketests::{pnpm_path, require_dotnet, require_pnpm, workspace_root, Smoketest};
use spacetimedb_smoketests::{pnpm_path, require_dotnet, require_emscripten, require_pnpm, workspace_root, Smoketest};
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
Expand Down Expand Up @@ -438,6 +438,37 @@ fn user_input_direct(ctx: &DbConnection) {
std::thread::sleep(std::time::Duration::from_secs(1));
std::process::exit(0);
}
"#,
connected_str: "connected",
}
}

fn cpp() -> Self {
// C++ server uses Rust client (same as TypeScript pattern)
Self {
lang: "cpp",
client_lang: "rust",
server_file: "src/lib.cpp",
client_file: "src/main.rs",
module_bindings: "src/module_bindings",
run_cmd: &["cargo", "run"],
build_cmd: &["cargo", "build"],
replacements: &[
("user_input_loop(&ctx)", "user_input_direct(&ctx)"),
(".with_token(creds_store()", "//.with_token(creds_store()"),
],
extra_code: r#"
fn user_input_direct(ctx: &DbConnection) {
let mut line = String::new();
std::io::stdin().read_line(&mut line).expect("Failed to read from stdin.");
if let Some(name) = line.strip_prefix("/name ") {
ctx.reducers.set_name(name.to_string()).unwrap();
} else {
ctx.reducers.send_message(line).unwrap();
}
std::thread::sleep(std::time::Duration::from_secs(1));
std::process::exit(0);
}
"#,
connected_str: "connected",
}
Expand Down Expand Up @@ -782,3 +813,12 @@ fn test_quickstart_typescript() {
let mut qt = QuickstartTest::new(QuickstartConfig::typescript());
qt.run_quickstart().expect("TypeScript quickstart test failed");
}

/// Run the C++ quickstart for server (with Rust client).
#[test]
fn test_quickstart_cpp() {
require_emscripten!();

let mut qt = QuickstartTest::new(QuickstartConfig::cpp());
qt.run_quickstart().expect("C++ quickstart test failed");
}
Loading
Loading