xtask/tests/
mod.rs
1use crate::Backend;
2use cargo_metadata::{Metadata, MetadataCommand};
3use std::process::Command;
4use std::process::Stdio;
5
6#[derive(clap::Args, Debug)]
7pub(crate) struct TestArgs {
8 #[clap(default_value_t = Backend::All)]
10 backend: Backend,
11 #[clap(long = "no-integration-tests")]
13 no_integration_tests: bool,
14 #[clap(long = "no-doc-tests")]
16 no_doc_tests: bool,
17 #[clap(long = "no-example-schema-check")]
19 no_example_schema_check: bool,
20 #[clap(long = "keep-going")]
23 keep_going: bool,
24 #[clap(long = "wasm")]
26 wasm: bool,
27 flags: Vec<String>,
34}
35
36impl TestArgs {
37 pub(crate) fn run(mut self) {
38 let metadata = MetadataCommand::default().exec().unwrap();
39 let success = if matches!(self.backend, Backend::All) {
40 let mut success = true;
41 for backend in Backend::ALL {
42 self.backend = *backend;
43 let result = self.run_tests(&metadata);
44 success = success && result;
45 if !result && !self.keep_going {
46 break;
47 }
48 }
49 success
50 } else {
51 self.run_tests(&metadata)
52 };
53 if !success {
54 std::process::exit(1);
55 }
56 }
57
58 fn run_tests(&self, metadata: &Metadata) -> bool {
59 let backend_name = self.backend.to_string();
60 println!("Running tests for {backend_name}");
61 let exclude = crate::utils::get_exclude_for_backend(&backend_name, metadata);
62 if std::env::var("DATABASE_URL").is_err() {
63 match self.backend {
64 Backend::Postgres => {
65 if std::env::var("PG_DATABASE_URL").is_err() {
66 println!(
67 "Remember to set `PG_DATABASE_URL` for running the postgres tests"
68 );
69 }
70 }
71 Backend::Sqlite => {
72 if std::env::var("SQLITE_DATABASE_URL").is_err() {
73 println!(
74 "Remember to set `SQLITE_DATABASE_URL` for running the sqlite tests"
75 );
76 }
77 }
78 Backend::Mysql => {
79 if std::env::var("MYSQL_DATABASE_URL").is_err()
80 || std::env::var("MYSQL_UNIT_TEST_DATABASE_URL").is_err()
81 {
82 println!("Remember to set `MYSQL_DATABASE_URL` and `MYSQL_UNIT_TEST_DATABASE_URL` for running the mysql tests");
83 }
84 }
85 Backend::All => unreachable!(),
86 }
87 }
88 let backend = &self.backend;
89 if self.wasm {
90 if matches!(backend, Backend::Sqlite) {
91 let mut command = Command::new("wasm-pack");
92 let out = command
93 .args(["test", "--chrome", "--headless", "--features", "sqlite"])
94 .current_dir(metadata.workspace_root.join("diesel"))
95 .env("RUSTFLAGS", "--cfg getrandom_backend=\"wasm_js\"")
96 .stderr(Stdio::inherit())
97 .stdout(Stdio::inherit())
98 .status()
99 .unwrap();
100 if !out.success() {
101 eprintln!("Failed to run wasm diesel unit tests");
102 return false;
103 }
104 let mut command = Command::new("wasm-pack");
105 let out = command
106 .args(["test", "--chrome", "--headless", "--features", "sqlite"])
107 .current_dir(metadata.workspace_root.join("diesel_tests"))
108 .env("RUSTFLAGS", "--cfg getrandom_backend=\"wasm_js\"")
109 .stderr(Stdio::inherit())
110 .stdout(Stdio::inherit())
111 .status()
112 .unwrap();
113 if !out.success() {
114 eprintln!("Failed to run wasm integration tests");
115 return false;
116 }
117 return true;
118 } else {
119 eprintln!(
120 "Only the sqlite backend supports wasm for now, the current backend is {backend}"
121 );
122 return true;
123 }
124 }
125 let url = match backend {
126 Backend::Postgres => std::env::var("PG_DATABASE_URL"),
127 Backend::Sqlite => std::env::var("SQLITE_DATABASE_URL"),
128 Backend::Mysql => std::env::var("MYSQL_DATABASE_URL"),
129 Backend::All => unreachable!(),
130 };
131 let url = url
132 .or_else(|_| std::env::var("DATABASE_URL"))
133 .expect("DATABASE_URL is set for tests");
134
135 let mut command = Command::new("cargo");
137 command
138 .args(["run", "-p", "diesel_cli", "--no-default-features", "-F"])
139 .arg(backend.to_string())
140 .args(["--", "migration", "run", "--migration-dir"])
141 .arg(
142 metadata
143 .workspace_root
144 .join("migrations")
145 .join(backend.to_string()),
146 )
147 .arg("--database-url")
148 .arg(&url);
149 println!("Run database migration via `{command:?}`");
150 let status = command
151 .stdout(Stdio::inherit())
152 .stderr(Stdio::inherit())
153 .status()
154 .unwrap();
155 if !status.success() {
156 eprintln!("Failed to run migrations");
157 return false;
158 }
159
160 if !self.no_integration_tests {
161 let mut command = Command::new("cargo");
163 command
164 .args(["nextest", "run", "--workspace", "--no-default-features"])
165 .current_dir(&metadata.workspace_root)
166 .args(exclude)
167 .arg("-F")
168 .arg(format!("diesel/{backend}"))
169 .args(["-F", "diesel/extras"])
170 .arg("-F")
171 .arg(format!("diesel_derives/{backend}"))
172 .arg("-F")
173 .arg(format!("diesel_cli/{backend}"))
174 .arg("-F")
175 .arg(format!("migrations_macros/{backend}"))
176 .arg("-F")
177 .arg(format!("diesel_migrations/{backend}"))
178 .arg("-F")
179 .arg(format!("diesel_tests/{backend}"))
180 .arg("-F")
181 .arg(format!("diesel-dynamic-schema/{backend}"))
182 .args(&self.flags);
183
184 if matches!(self.backend, Backend::Mysql) {
185 command.args(["-j", "1"]);
187 }
188 println!("Running tests via `{command:?}`: ");
189
190 let out = command
191 .stderr(Stdio::inherit())
192 .stdout(Stdio::inherit())
193 .status()
194 .unwrap();
195 if !out.success() {
196 eprintln!("Failed to run integration tests");
197 return false;
198 }
199 } else {
200 println!("Integration tests skipped because `--no-integration-tests` was passed");
201 }
202 if !self.no_doc_tests {
203 let mut command = Command::new("cargo");
204
205 command
206 .current_dir(&metadata.workspace_root)
207 .args([
208 "test",
209 "--doc",
210 "--no-default-features",
211 "-p",
212 "diesel",
213 "-p",
214 "diesel_derives",
215 "-p",
216 "diesel_migrations",
217 "-p",
218 "diesel-dynamic-schema",
219 "-p",
220 "dsl_auto_type",
221 "-p",
222 "diesel_table_macro_syntax",
223 "-F",
224 "diesel/extras",
225 ])
226 .arg("-F")
227 .arg(format!("diesel/{backend}"))
228 .arg("-F")
229 .arg(format!("diesel_derives/{backend}"))
230 .arg("-F")
231 .arg(format!("diesel-dynamic-schema/{backend}"));
232 if matches!(backend, Backend::Mysql) {
233 command.args(["-j", "1"]);
235 }
236 println!("Running tests via `{command:?}`: ");
237 let status = command
238 .stdout(Stdio::inherit())
239 .stderr(Stdio::inherit())
240 .status()
241 .unwrap();
242 if !status.success() {
243 eprintln!("Failed to run doc tests");
244 return false;
245 }
246 } else {
247 println!("Doc tests are skipped because `--no-doc-tests` was passed");
248 }
249
250 if !self.no_example_schema_check {
251 let examples = metadata
252 .workspace_root
253 .join("examples")
254 .join(backend.to_string());
255 let temp_dir = if matches!(backend, Backend::Sqlite) {
256 Some(tempfile::tempdir().unwrap())
257 } else {
258 None
259 };
260 let mut fail = false;
261 for p in metadata
262 .workspace_packages()
263 .into_iter()
264 .filter(|p| p.manifest_path.starts_with(&examples))
265 {
266 let example_root = p.manifest_path.parent().unwrap();
267 if example_root.join("migrations").exists() {
268 let db_url = if matches!(backend, Backend::Sqlite) {
269 temp_dir
270 .as_ref()
271 .unwrap()
272 .path()
273 .join(&p.name)
274 .display()
275 .to_string()
276 } else {
277 let (start, end) = url.rsplit_once('/').unwrap();
281 let query = end.split_once('?').map(|(_, q)| q);
282
283 let mut url = format!("{start}/{}", p.name);
284 if let Some(query) = query {
285 url.push('?');
286 url.push_str(query);
287 }
288 url
289 };
290
291 let mut command = Command::new("cargo");
292 command
293 .current_dir(example_root)
294 .args(["run", "-p", "diesel_cli", "--no-default-features", "-F"])
295 .arg(backend.to_string())
296 .args(["--", "database", "reset", "--locked-schema"])
297 .env("DATABASE_URL", db_url);
298 println!(
299 "Check schema for example `{}` ({example_root}) with command `{command:?}`",
300 p.name,
301 );
302 let status = command.status().unwrap();
303 if !status.success() {
304 fail = true;
305 eprintln!("Failed to check example schema for `{}`", p.name);
306 if !self.keep_going {
307 return false;
308 }
309 }
310 }
311 }
312 if fail {
313 return false;
314 }
315 } else {
316 println!(
317 "Example schema check is skipped because `--no-example-schema-check` was passed"
318 );
319 }
320
321 true
322 }
323}