Skip to content

Commit c47e571

Browse files
RReversergefjon
andauthored
Avoid relying on global CLI in tests (#294)
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 <[email protected]> Co-authored-by: Phoebe Goldman <[email protected]>
1 parent 676dfbe commit c47e571

File tree

6 files changed

+58
-125
lines changed

6 files changed

+58
-125
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,6 @@ jobs:
3030

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

33-
- name: Install CLI tool
34-
run: |
35-
cargo install --path crates/cli
36-
3733
- name: Create /stdb dir
3834
run: |
3935
sudo mkdir /stdb

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cli/src/config.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,13 @@ impl Config {
850850
}
851851
}
852852

853+
pub fn new_with_localhost() -> Self {
854+
Self {
855+
home: RawConfig::new_with_localhost(),
856+
proj: RawConfig::default(),
857+
}
858+
}
859+
853860
pub fn save(&self) {
854861
let config_path = if let Some(config_path) = std::env::var_os("SPACETIME_CONFIG_FILE") {
855862
PathBuf::from(&config_path)

crates/testing/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ spacetimedb-standalone = { path = "../standalone", version = "0.6.1" }
1111
spacetimedb-client-api = { path = "../client-api", version = "0.6.1" }
1212

1313
anyhow.workspace = true
14+
clap.workspace = true
1415
serde_json.workspace = true
1516
tokio.workspace = true
1617
wasmbin.workspace = true

crates/testing/src/lib.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
use clap::Command;
12
use spacetimedb::config::{FilesLocal, SpacetimeDbFiles};
3+
use spacetimedb_cli::Config;
24
use std::env;
35

46
pub mod modules;
@@ -17,3 +19,21 @@ pub fn set_key_env_vars(paths: &FilesLocal) {
1719
set_if_not_exist("SPACETIMEDB_JWT_PUB_KEY", paths.public_key());
1820
set_if_not_exist("SPACETIMEDB_JWT_PRIV_KEY", paths.private_key());
1921
}
22+
23+
pub fn invoke_cli(args: &[&str]) {
24+
lazy_static::lazy_static! {
25+
static ref RUNTIME: tokio::runtime::Runtime = tokio::runtime::Builder::new_multi_thread()
26+
.enable_all()
27+
.build()
28+
.unwrap();
29+
static ref COMMAND: Command = Command::new("spacetime").no_binary_name(true).subcommands(spacetimedb_cli::get_subcommands());
30+
static ref CONFIG: Config = Config::new_with_localhost();
31+
}
32+
33+
let args = COMMAND.clone().get_matches_from(args);
34+
let (cmd, args) = args.subcommand().expect("Could not split subcommand and args");
35+
36+
RUNTIME
37+
.block_on(spacetimedb_cli::exec_subcommand((*CONFIG).clone(), cmd, args))
38+
.unwrap()
39+
}

crates/testing/src/sdk.rs

Lines changed: 29 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,27 @@
1-
use duct::{cmd, Handle};
1+
use duct::cmd;
22
use lazy_static::lazy_static;
33
use rand::distributions::{Alphanumeric, DistString};
4+
use std::thread::JoinHandle;
45
use std::{collections::HashSet, fs::create_dir_all, sync::Mutex};
56

7+
use crate::invoke_cli;
68
use crate::modules::{module_path, CompiledModule};
79
use std::path::Path;
810

9-
struct StandaloneProcess {
10-
handle: Handle,
11-
num_using: usize,
12-
}
13-
14-
impl StandaloneProcess {
15-
fn start() -> Self {
16-
let handle = cmd!("spacetime", "start")
17-
.stderr_to_stdout()
18-
.stdout_capture()
19-
.unchecked()
20-
.start()
21-
.expect("Failed to run `spacetime start`");
22-
23-
StandaloneProcess { handle, num_using: 1 }
11+
pub fn ensure_standalone_process() {
12+
lazy_static! {
13+
static ref JOIN_HANDLE: Mutex<Option<JoinHandle<()>>> =
14+
Mutex::new(Some(std::thread::spawn(|| invoke_cli(&["start"]))));
2415
}
2516

26-
fn stop(&mut self) -> anyhow::Result<()> {
27-
assert!(self.num_using == 0);
28-
29-
self.handle.kill()?;
17+
let mut join_handle = JOIN_HANDLE.lock().unwrap();
3018

31-
Ok(())
32-
}
33-
34-
fn running_or_err(&self) -> anyhow::Result<()> {
35-
if let Some(output) = self
36-
.handle
37-
.try_wait()
38-
.expect("Error from spacetime standalone subprocess")
39-
{
40-
let code = output.status;
41-
let output = String::from_utf8_lossy(&output.stdout);
42-
Err(anyhow::anyhow!(
43-
"spacetime start exited unexpectedly. Exit status: {}. Output:\n{}",
44-
code,
45-
output,
46-
))
47-
} else {
48-
Ok(())
49-
}
50-
}
51-
52-
fn add_user(&mut self) -> anyhow::Result<()> {
53-
self.running_or_err()?;
54-
self.num_using += 1;
55-
Ok(())
56-
}
57-
58-
/// Returns true if the process was stopped because no one is using it.
59-
fn sub_user(&mut self) -> anyhow::Result<bool> {
60-
self.num_using -= 1;
61-
if self.num_using == 0 {
62-
self.stop()?;
63-
Ok(true)
64-
} else {
65-
Ok(false)
66-
}
67-
}
68-
}
69-
70-
static STANDALONE_PROCESS: Mutex<Option<StandaloneProcess>> = Mutex::new(None);
71-
72-
/// An RAII handle on the `STANDALONE_PROCESS`.
73-
///
74-
/// On construction, ensures that the `STANDALONE_PROCESS` is running.
75-
///
76-
/// On drop, checks to see if it was the last `StandaloneHandle`, and if so,
77-
/// terminates the `STANDALONE_PROCESS`.
78-
pub struct StandaloneHandle {
79-
_hidden: (),
80-
}
81-
82-
impl Default for StandaloneHandle {
83-
fn default() -> Self {
84-
let mut process = STANDALONE_PROCESS.lock().expect("STANDALONE_PROCESS Mutex is poisoned");
85-
if let Some(proc) = &mut *process {
86-
proc.add_user()
87-
.expect("Failed to add user for running spacetime standalone process");
88-
} else {
89-
*process = Some(StandaloneProcess::start());
90-
}
91-
StandaloneHandle { _hidden: () }
92-
}
93-
}
94-
95-
impl Drop for StandaloneHandle {
96-
fn drop(&mut self) {
97-
let mut process = STANDALONE_PROCESS.lock().expect("STANDALONE_PROCESS Mutex is poisoned");
98-
if let Some(proc) = &mut *process {
99-
if proc
100-
.sub_user()
101-
.expect("Failed to remove user for running spacetime standalone process")
102-
{
103-
*process = None;
104-
}
105-
}
19+
if join_handle
20+
.as_ref()
21+
.expect("Standalone process already finished")
22+
.is_finished()
23+
{
24+
join_handle.take().unwrap().join().expect("Standalone process failed");
10625
}
10726
}
10827

@@ -180,7 +99,7 @@ impl Test {
18099
TestBuilder::default()
181100
}
182101
pub fn run(&self) {
183-
let _handle = StandaloneHandle::default();
102+
ensure_standalone_process();
184103

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

@@ -189,12 +108,11 @@ impl Test {
189108
compiled.path(),
190109
&self.client_project,
191110
&self.generate_subdir,
192-
&self.name,
193111
);
194112

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

197-
let db_name = publish_module(&self.module_name, &self.name);
115+
let db_name = publish_module(&self.module_name);
198116

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

219-
fn publish_module(module: &str, test_name: &str) -> String {
137+
fn publish_module(module: &str) -> String {
220138
let name = random_module_name();
221-
let output = cmd!("spacetime", "publish", "--skip_clippy", name.clone(),)
222-
.stderr_to_stdout()
223-
.stdout_capture()
224-
.dir(module_path(module))
225-
.unchecked()
226-
.run()
227-
.expect("Error running spacetime publish");
228-
229-
status_ok_or_panic(output, "spacetime publish", test_name);
139+
invoke_cli(&[
140+
"publish",
141+
"--project-path",
142+
module_path(module).to_str().unwrap(),
143+
"--skip_clippy",
144+
&name,
145+
]);
230146

231147
name
232148
}
233149

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

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

247163
create_dir_all(&generate_dir).expect("Error creating generate subdir");
248-
let output = cmd!(
249-
"spacetime",
164+
invoke_cli(&[
250165
"generate",
251166
"--skip_clippy",
252167
"--lang",
253168
language,
254169
"--wasm-file",
255-
path,
170+
path.to_str().unwrap(),
256171
"--out-dir",
257-
generate_dir
258-
)
259-
.stderr_to_stdout()
260-
.stdout_capture()
261-
.unchecked()
262-
.run()
263-
.expect("Error running spacetime generate");
264-
265-
status_ok_or_panic(output, "spacetime generate", test_name);
172+
&generate_dir,
173+
]);
266174
}
267175

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

0 commit comments

Comments
 (0)