Skip to content

Commit 6958c24

Browse files
committed
Permit more than one runtime
Signed-off-by: Ryan Levick <[email protected]>
1 parent fd24870 commit 6958c24

File tree

6 files changed

+137
-40
lines changed

6 files changed

+137
-40
lines changed

tests/runtime-tests/src/lib.rs

Lines changed: 96 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
use anyhow::Context;
22
use std::path::{Path, PathBuf};
33
use testing_framework::{
4-
EnvTemplate, OnTestError, ServicesConfig, Spin, TestEnvironment, TestEnvironmentConfig,
5-
TestError, TestResult,
4+
EnvTemplate, InMemorySpin, OnTestError, Runtime, ServicesConfig, Spin, SpinConfig,
5+
TestEnvironment, TestEnvironmentConfig, TestError, TestResult,
66
};
77

88
/// Configuration for a runtime test
9-
pub struct RuntimeTestConfig {
9+
pub struct RuntimeTestConfig<R: Runtime> {
1010
pub test_path: PathBuf,
11-
pub spin_binary: PathBuf,
11+
pub runtime_config: R::Config,
1212
pub on_error: OnTestError,
1313
}
1414

@@ -43,18 +43,20 @@ impl RuntimeTest<Spin> {
4343

4444
let config = RuntimeTestConfig {
4545
test_path: test.path(),
46-
spin_binary: spin_binary.clone(),
46+
runtime_config: SpinConfig {
47+
binary_path: spin_binary.clone(),
48+
},
4749
on_error,
4850
};
49-
RuntimeTest::bootstrap(config)?.run();
51+
Self::bootstrap(config)?.run();
5052
}
5153
Ok(())
5254
}
5355

54-
pub fn bootstrap(config: RuntimeTestConfig) -> anyhow::Result<Self> {
56+
pub fn bootstrap(config: RuntimeTestConfig<Spin>) -> anyhow::Result<Self> {
5557
log::info!("Testing: {}", config.test_path.display());
5658
let test_path_clone = config.test_path.to_owned();
57-
let spin_binary = config.spin_binary.clone();
59+
let spin_binary = config.runtime_config.binary_path.clone();
5860
let preboot = move |env: &mut TestEnvironment<Spin>| {
5961
copy_manifest(&test_path_clone, env)?;
6062
Ok(())
@@ -77,6 +79,91 @@ impl RuntimeTest<Spin> {
7779

7880
/// Run an individual test
7981
pub fn run(&mut self) {
82+
self.run_test(|env| {
83+
let runtime = env.runtime_mut();
84+
let request = testing_framework::Request::new(reqwest::Method::GET, "/");
85+
let response = runtime.make_http_request(request)?;
86+
if response.status() == 200 {
87+
return Ok(());
88+
}
89+
if response.status() != 500 {
90+
return Err(anyhow::anyhow!("Runtime tests are expected to return either either a 200 or a 500, but it returned a {}", response.status()).into());
91+
}
92+
let text = response
93+
.text()
94+
.context("could not get runtime test HTTP response")?;
95+
if text.is_empty() {
96+
let stderr = runtime.stderr();
97+
return Err(anyhow::anyhow!("Runtime tests are expected to return a response body, but the response body was empty.\nstderr:\n{stderr}").into());
98+
}
99+
100+
Err(TestError::Failure(RuntimeTestFailure {
101+
error: text,
102+
stderr: runtime.stderr().to_owned(),
103+
}))
104+
})
105+
}
106+
}
107+
108+
impl RuntimeTest<InMemorySpin> {
109+
/// Run the runtime tests suite.
110+
///
111+
/// Error represents an error in bootstrapping the tests. What happens on individual test failures
112+
/// is controlled by `on_error`.
113+
pub fn run_all(tests_path: &Path, on_error: OnTestError) -> anyhow::Result<()> {
114+
for test in std::fs::read_dir(tests_path)
115+
.with_context(|| format!("failed to read test directory '{}'", tests_path.display()))?
116+
{
117+
let test = test.context("I/O error reading entry from test directory")?;
118+
if !test.file_type()?.is_dir() {
119+
log::debug!(
120+
"Ignoring non-sub-directory in test directory: {}",
121+
test.path().display()
122+
);
123+
continue;
124+
}
125+
126+
let config = RuntimeTestConfig {
127+
test_path: test.path(),
128+
runtime_config: (),
129+
on_error,
130+
};
131+
Self::bootstrap(config)?.run();
132+
}
133+
Ok(())
134+
}
135+
136+
pub fn bootstrap(config: RuntimeTestConfig<InMemorySpin>) -> anyhow::Result<Self> {
137+
log::info!("Testing: {}", config.test_path.display());
138+
let test_path_clone = config.test_path.to_owned();
139+
let preboot = move |env: &mut TestEnvironment<InMemorySpin>| {
140+
copy_manifest(&test_path_clone, env)?;
141+
Ok(())
142+
};
143+
let services_config = services_config(&config)?;
144+
let env_config = TestEnvironmentConfig::in_memory(services_config, preboot);
145+
let env = TestEnvironment::up(env_config)?;
146+
Ok(Self {
147+
test_path: config.test_path,
148+
on_error: config.on_error,
149+
env,
150+
})
151+
}
152+
153+
fn run(&mut self) {
154+
self.run_test(|env| {
155+
let runtime = env.runtime_mut();
156+
todo!()
157+
})
158+
}
159+
}
160+
161+
impl<R> RuntimeTest<R> {
162+
/// Run an individual test
163+
pub(crate) fn run_test(
164+
&mut self,
165+
test: impl FnOnce(&mut TestEnvironment<R>) -> TestResult<RuntimeTestFailure>,
166+
) {
80167
let on_error = self.on_error;
81168
// macro which will look at `on_error` and do the right thing
82169
macro_rules! error {
@@ -141,7 +228,7 @@ impl RuntimeTest<Spin> {
141228
}
142229
}
143230

144-
fn services_config(config: &RuntimeTestConfig) -> anyhow::Result<ServicesConfig> {
231+
fn services_config<R: Runtime>(config: &RuntimeTestConfig<R>) -> anyhow::Result<ServicesConfig> {
145232
let required_services = required_services(&config.test_path)?;
146233
let services_config = ServicesConfig::new(required_services)?;
147234
Ok(services_config)
@@ -179,30 +266,6 @@ fn copy_manifest<R>(test_dir: &Path, env: &mut TestEnvironment<R>) -> anyhow::Re
179266
Ok(())
180267
}
181268

182-
fn test(env: &mut TestEnvironment<Spin>) -> TestResult<RuntimeTestFailure> {
183-
let runtime = env.runtime_mut();
184-
let request = testing_framework::Request::new(reqwest::Method::GET, "/");
185-
let response = runtime.make_http_request(request)?;
186-
if response.status() == 200 {
187-
return Ok(());
188-
}
189-
if response.status() != 500 {
190-
return Err(anyhow::anyhow!("Runtime tests are expected to return either either a 200 or a 500, but it returned a {}", response.status()).into());
191-
}
192-
let text = response
193-
.text()
194-
.context("could not get runtime test HTTP response")?;
195-
if text.is_empty() {
196-
let stderr = runtime.stderr();
197-
return Err(anyhow::anyhow!("Runtime tests are expected to return a response body, but the response body was empty.\nstderr:\n{stderr}").into());
198-
}
199-
200-
Err(TestError::Failure(RuntimeTestFailure {
201-
error: text,
202-
stderr: runtime.stderr().to_owned(),
203-
}))
204-
}
205-
206269
/// A runtime test failure
207270
struct RuntimeTestFailure {
208271
/// The error message returned by the runtime

tests/runtime-tests/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::path::PathBuf;
22

33
use runtime_tests::RuntimeTest;
4-
use testing_framework::OnTestError;
4+
use testing_framework::{OnTestError, Spin};
55

66
fn main() -> anyhow::Result<()> {
77
env_logger::init();
@@ -12,5 +12,5 @@ fn main() -> anyhow::Result<()> {
1212
.unwrap_or_else(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests"));
1313

1414
let config = OnTestError::Log;
15-
RuntimeTest::run_all(&tests_path, spin_binary_path, config)
15+
RuntimeTest::<Spin>::run_all(&tests_path, spin_binary_path, config)
1616
}

tests/runtime.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ mod runtime_tests {
66
// generates individual tests for each one.
77
test_codegen_macro::codegen_runtime_tests!(
88
ignore: [
9-
// This test is flacky. Often gets "Connection reset by peer" errors.
9+
// This test is flaky. Often gets "Connection reset by peer" errors.
1010
// https://github.com/fermyon/spin/issues/2265
1111
"outbound-postgres"
1212
]
@@ -15,10 +15,12 @@ mod runtime_tests {
1515
fn run(test_path: PathBuf) {
1616
let config = runtime_tests::RuntimeTestConfig {
1717
test_path,
18-
spin_binary: env!("CARGO_BIN_EXE_spin").into(),
18+
runtime_config: testing_framework::SpinConfig {
19+
binary_path: env!("CARGO_BIN_EXE_spin").into(),
20+
},
1921
on_error: testing_framework::OnTestError::Panic,
2022
};
21-
runtime_tests::RuntimeTest::bootstrap(config)
23+
runtime_tests::RuntimeTest::<testing_framework::Spin>::bootstrap(config)
2224
.expect("failed to bootstrap runtime tests tests")
2325
.run();
2426
}

tests/testing-framework/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ mod test_environment;
1212

1313
pub use manifest_template::EnvTemplate;
1414
pub use services::ServicesConfig;
15-
pub use spin::{Request, Response, Spin, SpinMode};
16-
pub use test_environment::{TestEnvironment, TestEnvironmentConfig};
15+
pub use spin::{Request, Response, Spin, SpinConfig, SpinMode};
16+
pub use test_environment::{InMemorySpin, TestEnvironment, TestEnvironmentConfig};
1717

1818
#[derive(Debug, Clone, Copy)]
1919
/// What to do when a test errors
@@ -26,6 +26,7 @@ pub enum OnTestError {
2626

2727
/// A runtime which can be tested
2828
pub trait Runtime {
29+
type Config;
2930
/// Return an error if one has occurred
3031
fn error(&mut self) -> anyhow::Result<()>;
3132
}

tests/testing-framework/src/spin.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,8 @@ impl Drop for Spin {
244244
}
245245

246246
impl Runtime for Spin {
247+
type Config = SpinConfig;
248+
247249
fn error(&mut self) -> anyhow::Result<()> {
248250
if !matches!(self.io_mode, IoMode::None) && self.try_wait()?.is_some() {
249251
anyhow::bail!("Spin exited early: {}", self.stderr());
@@ -253,6 +255,10 @@ impl Runtime for Spin {
253255
}
254256
}
255257

258+
pub struct SpinConfig {
259+
pub binary_path: std::path::PathBuf,
260+
}
261+
256262
fn kill_process(process: &mut std::process::Child) {
257263
#[cfg(windows)]
258264
{

tests/testing-framework/src/test_environment.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,28 @@ impl TestEnvironmentConfig<Spin> {
191191
}
192192
}
193193
}
194+
195+
pub struct InMemorySpin;
196+
197+
impl Runtime for InMemorySpin {
198+
type Config = ();
199+
200+
fn error(&mut self) -> anyhow::Result<()> {
201+
Ok(())
202+
}
203+
}
204+
205+
impl TestEnvironmentConfig<InMemorySpin> {
206+
pub fn in_memory(
207+
services_config: ServicesConfig,
208+
preboot: impl FnOnce(&mut TestEnvironment<InMemorySpin>) -> anyhow::Result<()> + 'static,
209+
) -> Self {
210+
Self {
211+
services_config,
212+
create_runtime: Box::new(|env| {
213+
preboot(env)?;
214+
Ok(InMemorySpin)
215+
}),
216+
}
217+
}
218+
}

0 commit comments

Comments
 (0)