Skip to content

Commit

Permalink
Avoid relying on global CLI in tests (#294)
Browse files Browse the repository at this point in the history
This switches tests from invoking a spacetimedb CLI found on PATH to always using up-to-date version by depending on the CLI crate as a library.

---------

Signed-off-by: Phoebe Goldman <phoebe@goldman-tribe.org>
Co-authored-by: Phoebe Goldman <phoebe@clockworklabs.io>
  • Loading branch information
RReverser and gefjon authored Sep 14, 2023
1 parent 676dfbe commit c47e571
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 125 deletions.
4 changes: 0 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ jobs:

- uses: dsherret/rust-toolchain-file@v1

- name: Install CLI tool
run: |
cargo install --path crates/cli
- name: Create /stdb dir
run: |
sudo mkdir /stdb
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions crates/cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,13 @@ impl Config {
}
}

pub fn new_with_localhost() -> Self {
Self {
home: RawConfig::new_with_localhost(),
proj: RawConfig::default(),
}
}

pub fn save(&self) {
let config_path = if let Some(config_path) = std::env::var_os("SPACETIME_CONFIG_FILE") {
PathBuf::from(&config_path)
Expand Down
1 change: 1 addition & 0 deletions crates/testing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ spacetimedb-standalone = { path = "../standalone", version = "0.6.1" }
spacetimedb-client-api = { path = "../client-api", version = "0.6.1" }

anyhow.workspace = true
clap.workspace = true
serde_json.workspace = true
tokio.workspace = true
wasmbin.workspace = true
Expand Down
20 changes: 20 additions & 0 deletions crates/testing/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use clap::Command;
use spacetimedb::config::{FilesLocal, SpacetimeDbFiles};
use spacetimedb_cli::Config;
use std::env;

pub mod modules;
Expand All @@ -17,3 +19,21 @@ pub fn set_key_env_vars(paths: &FilesLocal) {
set_if_not_exist("SPACETIMEDB_JWT_PUB_KEY", paths.public_key());
set_if_not_exist("SPACETIMEDB_JWT_PRIV_KEY", paths.private_key());
}

pub fn invoke_cli(args: &[&str]) {
lazy_static::lazy_static! {
static ref RUNTIME: tokio::runtime::Runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
static ref COMMAND: Command = Command::new("spacetime").no_binary_name(true).subcommands(spacetimedb_cli::get_subcommands());
static ref CONFIG: Config = Config::new_with_localhost();
}

let args = COMMAND.clone().get_matches_from(args);
let (cmd, args) = args.subcommand().expect("Could not split subcommand and args");

RUNTIME
.block_on(spacetimedb_cli::exec_subcommand((*CONFIG).clone(), cmd, args))
.unwrap()
}
150 changes: 29 additions & 121 deletions crates/testing/src/sdk.rs
Original file line number Diff line number Diff line change
@@ -1,108 +1,27 @@
use duct::{cmd, Handle};
use duct::cmd;
use lazy_static::lazy_static;
use rand::distributions::{Alphanumeric, DistString};
use std::thread::JoinHandle;
use std::{collections::HashSet, fs::create_dir_all, sync::Mutex};

use crate::invoke_cli;
use crate::modules::{module_path, CompiledModule};
use std::path::Path;

struct StandaloneProcess {
handle: Handle,
num_using: usize,
}

impl StandaloneProcess {
fn start() -> Self {
let handle = cmd!("spacetime", "start")
.stderr_to_stdout()
.stdout_capture()
.unchecked()
.start()
.expect("Failed to run `spacetime start`");

StandaloneProcess { handle, num_using: 1 }
pub fn ensure_standalone_process() {
lazy_static! {
static ref JOIN_HANDLE: Mutex<Option<JoinHandle<()>>> =
Mutex::new(Some(std::thread::spawn(|| invoke_cli(&["start"]))));
}

fn stop(&mut self) -> anyhow::Result<()> {
assert!(self.num_using == 0);

self.handle.kill()?;
let mut join_handle = JOIN_HANDLE.lock().unwrap();

Ok(())
}

fn running_or_err(&self) -> anyhow::Result<()> {
if let Some(output) = self
.handle
.try_wait()
.expect("Error from spacetime standalone subprocess")
{
let code = output.status;
let output = String::from_utf8_lossy(&output.stdout);
Err(anyhow::anyhow!(
"spacetime start exited unexpectedly. Exit status: {}. Output:\n{}",
code,
output,
))
} else {
Ok(())
}
}

fn add_user(&mut self) -> anyhow::Result<()> {
self.running_or_err()?;
self.num_using += 1;
Ok(())
}

/// Returns true if the process was stopped because no one is using it.
fn sub_user(&mut self) -> anyhow::Result<bool> {
self.num_using -= 1;
if self.num_using == 0 {
self.stop()?;
Ok(true)
} else {
Ok(false)
}
}
}

static STANDALONE_PROCESS: Mutex<Option<StandaloneProcess>> = Mutex::new(None);

/// An RAII handle on the `STANDALONE_PROCESS`.
///
/// On construction, ensures that the `STANDALONE_PROCESS` is running.
///
/// On drop, checks to see if it was the last `StandaloneHandle`, and if so,
/// terminates the `STANDALONE_PROCESS`.
pub struct StandaloneHandle {
_hidden: (),
}

impl Default for StandaloneHandle {
fn default() -> Self {
let mut process = STANDALONE_PROCESS.lock().expect("STANDALONE_PROCESS Mutex is poisoned");
if let Some(proc) = &mut *process {
proc.add_user()
.expect("Failed to add user for running spacetime standalone process");
} else {
*process = Some(StandaloneProcess::start());
}
StandaloneHandle { _hidden: () }
}
}

impl Drop for StandaloneHandle {
fn drop(&mut self) {
let mut process = STANDALONE_PROCESS.lock().expect("STANDALONE_PROCESS Mutex is poisoned");
if let Some(proc) = &mut *process {
if proc
.sub_user()
.expect("Failed to remove user for running spacetime standalone process")
{
*process = None;
}
}
if join_handle
.as_ref()
.expect("Standalone process already finished")
.is_finished()
{
join_handle.take().unwrap().join().expect("Standalone process failed");
}
}

Expand Down Expand Up @@ -180,7 +99,7 @@ impl Test {
TestBuilder::default()
}
pub fn run(&self) {
let _handle = StandaloneHandle::default();
ensure_standalone_process();

let compiled = CompiledModule::compile(&self.module_name);

Expand All @@ -189,12 +108,11 @@ impl Test {
compiled.path(),
&self.client_project,
&self.generate_subdir,
&self.name,
);

compile_client(&self.compile_command, &self.client_project, &self.name);

let db_name = publish_module(&self.module_name, &self.name);
let db_name = publish_module(&self.module_name);

run_client(&self.run_command, &self.client_project, &db_name, &self.name);
}
Expand All @@ -216,22 +134,20 @@ fn random_module_name() -> String {
Alphanumeric.sample_string(&mut rand::thread_rng(), 16)
}

fn publish_module(module: &str, test_name: &str) -> String {
fn publish_module(module: &str) -> String {
let name = random_module_name();
let output = cmd!("spacetime", "publish", "--skip_clippy", name.clone(),)
.stderr_to_stdout()
.stdout_capture()
.dir(module_path(module))
.unchecked()
.run()
.expect("Error running spacetime publish");

status_ok_or_panic(output, "spacetime publish", test_name);
invoke_cli(&[
"publish",
"--project-path",
module_path(module).to_str().unwrap(),
"--skip_clippy",
&name,
]);

name
}

fn generate_bindings(language: &str, path: &Path, client_project: &str, generate_subdir: &str, test_name: &str) {
fn generate_bindings(language: &str, path: &Path, client_project: &str, generate_subdir: &str) {
let generate_dir = format!("{}/{}", client_project, generate_subdir);

let mut bindings_lock = BINDINGS_GENERATED.lock().expect("BINDINGS_GENERATED Mutex is poisoned");
Expand All @@ -245,24 +161,16 @@ fn generate_bindings(language: &str, path: &Path, client_project: &str, generate
}

create_dir_all(&generate_dir).expect("Error creating generate subdir");
let output = cmd!(
"spacetime",
invoke_cli(&[
"generate",
"--skip_clippy",
"--lang",
language,
"--wasm-file",
path,
path.to_str().unwrap(),
"--out-dir",
generate_dir
)
.stderr_to_stdout()
.stdout_capture()
.unchecked()
.run()
.expect("Error running spacetime generate");

status_ok_or_panic(output, "spacetime generate", test_name);
&generate_dir,
]);
}

fn split_command_string(command: &str) -> (&str, Vec<&str>) {
Expand Down

0 comments on commit c47e571

Please sign in to comment.