Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

637 linesCodeBlameRaw
1//! Exercise the production transition SQL with interleaved attempts.
2use rusqlite::{Connection, OptionalExtension, params};
3
4use super::{
5 ABANDON_LANDING, ACCEPT_BUILD, BEGIN_LANDING, CLAIM_BUILD, ENQUEUE, FINISH_CHECKS,
6 FINISH_LANDING, LEAVE, PUBLISHING, REPORT_BUILD, RETEST, RETESTED, STILL_LANDING,
7 attempt_branch,
8};
9use crate::integration::{FINISH_PROBE, REPORT_PROBE, RESOLVE_HELD};
10
11fn database() -> Connection {
12 let db = crate::migrated::database();
13 db.execute_batch("INSERT INTO pulls (id, repo_id, number, title, agent, runtime, author_id, author_name, status, created_at, updated_at)
14 VALUES ('pull', 'repo', 1, 'Change', 'codex', 'external', 'user', 'user', 'open', 'now', 'now');
15 INSERT INTO queue_entries (id, state, repo_id, pull_id, number, enqueued_by, created_at)
16 VALUES ('entry', 'waiting', 'repo', 'pull', 1, '{}', 'now');").unwrap();
17 db
18}
19
20#[test]
21fn repeated_admission_is_atomic() {
22 let db = database();
23 db.execute("DELETE FROM queue_entries", []).unwrap();
24 let enqueue = |id: &str| {
25 db.query_row(
26 ENQUEUE,
27 params![id, "repo", "pull", 1, "{}", 0, "later", 0, "merge", None::<String>, None::<String>],
28 |_| Ok(()),
29 )
30 .optional()
31 .unwrap()
32 .is_some()
33 };
34 assert!(enqueue("first"));
35 assert!(!enqueue("second"));
36 db.execute(
37 "UPDATE queue_entries SET state = 'removed' WHERE id = 'first'",
38 [],
39 )
40 .unwrap();
41 assert!(enqueue("third"));
42}
43
44#[test]
45fn stale_reset_and_held_failure_cannot_touch_a_replacement_attempt() {
46 let db = database();
47 assert!(claim(&db, "old"));
48 assert!(report(&db, "old", "old-commit"));
49 db.execute("UPDATE queue_entries SET held = 'failure'", [])
50 .unwrap();
51 assert_eq!(
52 db.execute(RETEST, params!["entry", "old"])
53 .unwrap(),
54 1
55 );
56 assert!(claim(&db, "new"));
57 assert!(report(&db, "new", "new-commit"));
58 db.execute("UPDATE queue_entries SET held = 'failure'", [])
59 .unwrap();
60 assert_eq!(
61 db.execute(RETEST, params!["entry", "old"])
62 .unwrap(),
63 0
64 );
65 assert!(
66 db.query_row(
67 RESOLVE_HELD,
68 params!["now", "entry", "failure", "old"],
69 |_| Ok(())
70 )
71 .optional()
72 .unwrap()
73 .is_none()
74 );
75 assert!(
76 db.query_row(
77 RESOLVE_HELD,
78 params!["now", "entry", "failure", "new"],
79 |_| Ok(())
80 )
81 .optional()
82 .unwrap()
83 .is_some()
84 );
85 assert_ne!(
86 attempt_branch("entry", Some("old")),
87 attempt_branch("entry", Some("new"))
88 );
89}
90
91#[test]
92fn removal_and_publication_have_one_winner_and_recovery_keeps_the_commit() {
93 let db = database();
94 assert!(claim(&db, "attempt"));
95 assert!(report(&db, "attempt", "tested"));
96 assert!(finish(&db, "tested"));
97 let publish = || {
98 db.query_row(BEGIN_LANDING, params!["entry", "tested", "attempt"], |_| {
99 Ok(())
100 })
101 .optional()
102 .unwrap()
103 .is_some()
104 };
105 assert!(publish());
106 assert!(publish(), "a crash resumes the same immutable publication");
107 assert!(
108 db.query_row(
109 LEAVE,
110 params!["removed", None::<String>, "now", "entry"],
111 |_| Ok(())
112 )
113 .optional()
114 .unwrap()
115 .is_none()
116 );
117 assert_eq!(
118 db.execute(RETEST, params!["entry", "attempt"])
119 .unwrap(),
120 0
121 );
122 assert!(
123 db.query_row(
124 BEGIN_LANDING,
125 params!["entry", "different", "attempt"],
126 |_| Ok(())
127 )
128 .optional()
129 .unwrap()
130 .is_none()
131 );
132 db.execute("UPDATE queue_entries SET landing_commit = NULL", [])
133 .unwrap();
134 assert!(
135 db.query_row(
136 LEAVE,
137 params!["removed", None::<String>, "now", "entry"],
138 |_| Ok(())
139 )
140 .optional()
141 .unwrap()
142 .is_some()
143 );
144 assert!(!publish());
145}
146
147#[test]
148fn removing_a_predecessor_invalidates_the_same_attempt_even_if_it_just_passed() {
149 let db = database();
150 assert!(claim(&db, "attempt"));
151 // Removal read this attempt while it was building; its result arrives
152 // before the dependent entry's reset reaches the database.
153 assert!(report(&db, "attempt", "includes-removed-predecessor"));
154 assert!(finish(&db, "includes-removed-predecessor"));
155 assert_eq!(db.execute(RETEST, params!["entry", "attempt"]).unwrap(), 1);
156 assert!(!finish(&db, "includes-removed-predecessor"));
157 assert!(claim(&db, "replacement"));
158 assert_eq!(db.execute(RETEST, params!["entry", "attempt"]).unwrap(), 0);
159}
160
161#[test]
162fn a_passing_entry_cannot_publish_ahead_of_an_unfinished_predecessor() {
163 let db = database();
164 assert!(claim(&db, "attempt"));
165 assert!(report(&db, "attempt", "tested"));
166 assert!(finish(&db, "tested"));
167 db.execute("INSERT INTO queue_entries (id, repo_id, pull_id, number, enqueued_by, state, created_at) VALUES ('earlier', 'repo', 'other', 2, '{}', 'testing', 'before')", []).unwrap();
168 db.execute("UPDATE queue_entries SET ahead = '[2]' WHERE id = 'entry'", []).unwrap();
169 assert!(
170 db.query_row(
171 BEGIN_LANDING,
172 params!["entry", "tested", "attempt"],
173 |_| Ok(())
174 )
175 .optional()
176 .unwrap()
177 .is_none()
178 );
179 db.execute("UPDATE queue_entries SET state = 'removed' WHERE id = 'earlier'", []).unwrap();
180 assert!(db.query_row(BEGIN_LANDING, params!["entry", "tested", "attempt"], |_| Ok(())).optional().unwrap().is_none(),
181 "removing the predecessor cannot publish a combination that still contains it");
182 db.execute(
183 "UPDATE queue_entries SET state = 'landed' WHERE id = 'earlier'",
184 [],
185 )
186 .unwrap();
187 assert!(
188 db.query_row(
189 BEGIN_LANDING,
190 params!["entry", "tested", "attempt"],
191 |_| Ok(())
192 )
193 .optional()
194 .unwrap()
195 .is_some()
196 );
197}
198
199#[test]
200fn delayed_probe_reports_and_timeouts_cannot_replace_accepted_results() {
201 let db = database();
202 db.execute("INSERT INTO queue_probes (id, repo_id, entry_id, number, subset, state, token_hash, created_at) VALUES ('probe', 'repo', 'entry', 1, '[]', 'testing', 'token', 'now')", []).unwrap();
203 assert!(
204 db.query_row(
205 REPORT_PROBE,
206 params!["tested", "probe", "token"],
207 |_| Ok(())
208 )
209 .optional()
210 .unwrap()
211 .is_some()
212 );
213 assert!(
214 db.query_row(
215 REPORT_PROBE,
216 params!["different", "probe", "token"],
217 |_| Ok(())
218 )
219 .optional()
220 .unwrap()
221 .is_none()
222 );
223 let finish = |token: Option<&str>, commit: Option<&str>| {
224 db.query_row(
225 FINISH_PROBE,
226 params![
227 "passed",
228 None::<String>,
229 "now",
230 "probe",
231 "testing",
232 token,
233 commit
234 ],
235 |_| Ok(()),
236 )
237 .optional()
238 .unwrap()
239 .is_some()
240 };
241 assert!(
242 !finish(Some("token"), None),
243 "timeout from before report acceptance"
244 );
245 assert!(!finish(None, Some("different")));
246 assert!(finish(None, Some("tested")));
247 assert!(!finish(None, Some("tested")));
248}
249
250fn claim(db: &Connection, token: &str) -> bool {
251 db.query_row(
252 CLAIM_BUILD,
253 params![token, "head", "base", "[]", "now", "entry", "[]"],
254 |_| Ok(()),
255 )
256 .optional()
257 .unwrap()
258 .is_some()
259}
260
261fn report(db: &Connection, token: &str, commit: &str) -> bool {
262 if db
263 .query_row(ACCEPT_BUILD, params![commit, "entry", token], |_| Ok(()))
264 .optional()
265 .unwrap()
266 .is_none()
267 {
268 return false;
269 }
270 db.query_row(
271 REPORT_BUILD,
272 params![
273 "testing",
274 commit,
275 "[]",
276 None::<String>,
277 None::<String>,
278 0,
279 "testing",
280 "now",
281 "entry",
282 token,
283 commit
284 ],
285 |_| Ok(()),
286 )
287 .optional()
288 .unwrap()
289 .is_some()
290}
291
292#[test]
293fn a_workflow_finishing_during_dispatch_finds_the_accepted_commit() {
294 let db = database();
295 assert!(claim(&db, "attempt"));
296 assert!(
297 db.query_row(ACCEPT_BUILD, params!["tested", "entry", "attempt"], |_| Ok(
298 ()
299 ))
300 .optional()
301 .unwrap()
302 .is_some()
303 );
304 assert!(
305 !finish(&db, "tested"),
306 "callbacks wait until every workflow is registered"
307 );
308 assert!(
309 db.query_row(
310 REPORT_BUILD,
311 params![
312 "testing",
313 "tested",
314 "[]",
315 None::<String>,
316 None::<String>,
317 0,
318 "testing",
319 "now",
320 "entry",
321 "attempt",
322 "tested"
323 ],
324 |_| Ok(())
325 )
326 .optional()
327 .unwrap()
328 .is_some()
329 );
330 // report_queue rereads the group's facts after opening this fence.
331 assert!(finish(&db, "tested"));
332 assert!(
333 db.query_row(
334 REPORT_BUILD,
335 params![
336 "testing",
337 "tested",
338 "[]",
339 None::<String>,
340 None::<String>,
341 0,
342 "testing",
343 "now",
344 "entry",
345 "attempt",
346 "tested"
347 ],
348 |_| Ok(())
349 )
350 .optional()
351 .unwrap()
352 .is_none()
353 );
354}
355
356fn finish(db: &Connection, commit: &str) -> bool {
357 db.query_row(
358 FINISH_CHECKS,
359 // As merge_group_finished binds it: the attempt of the row it read.
360 params!["passed", None::<String>, "passed", "now", "entry", commit, current_attempt(db)],
361 |_| Ok(()),
362 )
363 .optional()
364 .unwrap()
365 .is_some()
366}
367
368#[test]
369fn only_the_scheduler_that_claimed_an_entry_may_start_its_build() {
370 let db = database();
371 assert!(claim(&db, "first"));
372 assert!(!claim(&db, "second"));
373 assert!(!report(&db, "second", "wrong"));
374 assert!(report(&db, "first", "combined"));
375 assert!(!report(&db, "first", "combined"));
376 assert!(finish(&db, "combined"));
377 assert!(!finish(&db, "combined"));
378}
379
380#[test]
381fn a_result_read_before_removal_cannot_resurrect_the_entry() {
382 let db = database();
383 assert!(claim(&db, "first"));
384 // report_queue has read the token, then the user removes the entry.
385 db.execute("UPDATE queue_entries SET state = 'removed'", [])
386 .unwrap();
387 assert!(!report(&db, "first", "combined"));
388 assert_eq!(
389 db.query_row("SELECT state FROM queue_entries", [], |row| row
390 .get::<_, String>(0))
391 .unwrap(),
392 "removed"
393 );
394}
395
396#[test]
397fn an_old_build_cannot_overwrite_a_retry() {
398 let db = database();
399 assert!(claim(&db, "old"));
400 db.execute(
401 "UPDATE queue_entries SET state = 'waiting', token_hash = NULL",
402 [],
403 )
404 .unwrap();
405 assert!(!report(&db, "old", "old-commit"));
406 assert!(claim(&db, "new"));
407 assert!(!report(&db, "old", "old-commit"));
408 assert!(report(&db, "new", "new-commit"));
409 assert!(finish(&db, "new-commit"));
410}
411
412#[test]
413fn old_workflow_callbacks_cannot_pass_a_new_attempt() {
414 let db = database();
415 assert!(claim(&db, "old"));
416 assert!(report(&db, "old", "old-commit"));
417 // The callback read this row before a retry reset it.
418 db.execute(
419 "UPDATE queue_entries SET state = 'waiting', combined_commit = NULL",
420 [],
421 )
422 .unwrap();
423 assert!(claim(&db, "new"));
424 assert!(!finish(&db, "old-commit"));
425 assert!(report(&db, "new", "new-commit"));
426 assert!(!finish(&db, "old-commit"));
427 assert!(finish(&db, "new-commit"));
428}
429
430fn exists(db: &Connection, sql: &str, args: impl rusqlite::Params) -> bool {
431 db.query_row(sql, args, |_| Ok(())).optional().unwrap().is_some()
432}
433
434#[test]
435fn a_publisher_that_lost_the_race_cannot_undo_the_winners_landing() {
436 let db = database();
437 assert!(claim(&db, "attempt"));
438 assert!(report(&db, "attempt", "tested"));
439 assert!(finish(&db, "tested"));
440 // Two settle passes (a poll and a callback) both take the intent: it is
441 // resumable by design, so it is not exclusive.
442 assert!(exists(&db, BEGIN_LANDING, params!["entry", "tested", "attempt"]));
443 assert!(exists(&db, BEGIN_LANDING, params!["entry", "tested", "attempt"]));
444 // Mid-flight, the loser's failed compare-and-swap cannot tell it apart
445 // from the winner: it must ask Git again (settle) before abandoning.
446 assert!(exists(&db, STILL_LANDING, params!["entry", "tested"]));
447 // The winner records the merge and finishes, dropping the tested branch;
448 // the loser's retry then fails for want of that branch.
449 assert_eq!(db.execute(FINISH_LANDING, params!["now", "entry"]).unwrap(), 1);
450 assert!(!exists(&db, STILL_LANDING, params!["entry", "tested"]));
451 assert!(
452 !exists(&db, ABANDON_LANDING, params!["entry", "tested"]),
453 "only a holder of the intent may give it up, so nothing is retested"
454 );
455 assert_eq!(db.execute(RETEST, params!["entry", "attempt"]).unwrap(), 0);
456 assert_eq!(
457 db.query_row("SELECT state FROM queue_entries", [], |row| row.get::<_, String>(0)).unwrap(),
458 "landed"
459 );
460}
461
462#[test]
463fn a_genuinely_refused_publication_is_abandoned_once() {
464 let db = database();
465 assert!(claim(&db, "attempt"));
466 assert!(report(&db, "attempt", "tested"));
467 assert!(finish(&db, "tested"));
468 assert!(exists(&db, BEGIN_LANDING, params!["entry", "tested", "attempt"]));
469 assert!(exists(&db, PUBLISHING, params!["pull"]), "removal, closing and rebasing are refused");
470 assert!(exists(&db, ABANDON_LANDING, params!["entry", "tested"]));
471 assert!(!exists(&db, ABANDON_LANDING, params!["entry", "tested"]));
472 assert!(!exists(&db, PUBLISHING, params!["pull"]));
473 assert_eq!(db.execute(RETEST, params!["entry", "attempt"]).unwrap(), 1);
474}
475
476#[test]
477fn only_a_reset_attempt_gives_up_its_branch() {
478 let db = database();
479 assert!(claim(&db, "attempt"));
480 assert!(report(&db, "attempt", "tested"));
481 assert!(finish(&db, "tested"));
482 assert!(exists(&db, BEGIN_LANDING, params!["entry", "tested", "attempt"]));
483 // Being published: the reset does nothing and the branch must stay.
484 assert_eq!(db.execute(RETEST, params!["entry", "attempt"]).unwrap(), 0);
485 assert!(!exists(&db, RETESTED, params!["entry", "attempt"]));
486 assert!(exists(&db, ABANDON_LANDING, params!["entry", "tested"]));
487 assert_eq!(db.execute(RETEST, params!["entry", "attempt"]).unwrap(), 1);
488 assert!(exists(&db, RETESTED, params!["entry", "attempt"]));
489 // A newer attempt's branch is never the reset one's.
490 assert!(claim(&db, "next"));
491 assert!(!exists(&db, RETESTED, params!["entry", "attempt"]));
492}
493
494#[test]
495fn a_bypass_asked_for_at_admission_is_kept_for_landing() {
496 let db = database();
497 db.execute("DELETE FROM queue_entries", []).unwrap();
498 assert!(exists(&db, ENQUEUE, params!["first", "repo", "pull", 1, "{}", 0, "later", 1, "merge", None::<String>, None::<String>]));
499 assert_eq!(
500 db.query_row("SELECT bypass_rules FROM queue_entries", [], |row| row.get::<_, u32>(0)).unwrap(),
501 1
502 );
503 let person = g1t_contracts::User { id: "u".into(), username: "u".into(), ..Default::default() };
504 assert!(crate::rulesets::landing_honors_bypass(&person, true));
505 assert!(!crate::rulesets::landing_honors_bypass(&person, false));
506 assert!(crate::rulesets::landing_honors_bypass(&g1t_contracts::User::system("ns"), false));
507}
508
509fn current_attempt(db: &Connection) -> Option<String> {
510 db.query_row("SELECT attempt_id FROM queue_entries WHERE id = 'entry'", [], |row| row.get(0)).unwrap()
511}
512
513#[test]
514fn a_callback_read_for_an_earlier_attempt_cannot_finish_a_retry_on_the_same_commit() {
515 let db = database();
516 assert!(claim(&db, "old"));
517 assert!(report(&db, "old", "same"));
518 assert_eq!(db.execute(RETEST, params!["entry", "old"]).unwrap(), 1);
519 assert!(claim(&db, "new"));
520 assert!(report(&db, "new", "same"));
521 let finish_as = |attempt: &str| {
522 exists(&db, FINISH_CHECKS, params!["failed", None::<String>, "failed", "now", "entry", "same", attempt])
523 };
524 assert!(!finish_as("old"), "the row it read belonged to the earlier attempt");
525 assert!(finish_as("new"));
526}
527
528#[test]
529fn each_attempt_records_exactly_the_stack_it_built() {
530 let db = database();
531 let stack = |commit: &str| {
532 serde_json::json!([{ "number": 1, "title": "Change", "source": { "namespace": "ns", "name": "repo" },
533 "branch": "feature", "commit": commit }])
534 .to_string()
535 };
536 let claim_with = |token: &str, built: &str| {
537 exists(&db, CLAIM_BUILD, params![token, built, "base", "[]", "now", "entry", stack(built)])
538 };
539 assert!(claim_with("first", "c1"));
540 assert_eq!(db.execute(RETEST, params!["entry", "first"]).unwrap(), 1);
541 assert!(claim_with("second", "c2"));
542 let row: super::EntryRow = {
543 let mut statement = db.prepare("SELECT * FROM queue_entries WHERE id = 'entry'").unwrap();
544 let names: Vec<String> = statement.column_names().iter().map(|name| (*name).to_owned()).collect();
545 statement
546 .query_row([], |row| {
547 let mut object = serde_json::Map::new();
548 for (index, name) in names.iter().enumerate() {
549 let value: rusqlite::types::Value = row.get(index)?;
550 object.insert(name.clone(), match value {
551 rusqlite::types::Value::Null => serde_json::Value::Null,
552 rusqlite::types::Value::Integer(n) => n.into(),
553 rusqlite::types::Value::Real(n) => n.into(),
554 rusqlite::types::Value::Text(text) => text.into(),
555 rusqlite::types::Value::Blob(_) => serde_json::Value::Null,
556 });
557 }
558 Ok(serde_json::from_value(serde_json::Value::Object(object)).unwrap())
559 })
560 .unwrap()
561 };
562 let recorded = row.tested_stack().unwrap();
563 assert_eq!(recorded[0].commit, "c2", "the second attempt's own commits");
564 assert!(crate::integration::recorded_stack(recorded.clone(), 1, &[]).is_some());
565 assert!(crate::integration::recorded_stack(recorded, 1, &[7]).is_none(), "not the combination that failed");
566}
567
568#[test]
569fn an_entry_whose_branch_is_gone_leaves_instead_of_blocking_those_behind() {
570 let db = database();
571 db.execute("INSERT INTO pulls (id, repo_id, number, title, agent, runtime, author_id, author_name, status, created_at, updated_at)
572 VALUES ('later-pull', 'repo', 2, 'Next', 'codex', 'external', 'user', 'user', 'open', 'now', 'now')", []).unwrap();
573 db.execute("INSERT INTO queue_entries (id, state, repo_id, pull_id, number, enqueued_by, created_at, attempt_id, combined_commit)
574 VALUES ('later', 'passed', 'repo', 'later-pull', 2, '{}', 'then', 'a2', 'tested2')", []).unwrap();
575 // 'entry' is waiting at the front and can never be built.
576 assert!(!exists(&db, BEGIN_LANDING, params!["later", "tested2", "a2"]));
577 assert!(exists(&db, LEAVE, params!["removed", super::BRANCH_GONE, "now", "entry"]));
578 assert_eq!(
579 db.query_row("SELECT error FROM queue_entries WHERE id = 'entry'", [], |row| row.get::<_, String>(0)).unwrap(),
580 "Its branch no longer exists."
581 );
582 assert!(exists(&db, BEGIN_LANDING, params!["later", "tested2", "a2"]));
583 assert_eq!(
584 super::gone_note(super::BRANCH_GONE),
585 "its branch no longer exists, so it cannot be tested or landed. Push the branch again and merge to rejoin."
586 );
587}
588
589#[test]
590fn an_entry_keeps_how_it_lands_and_one_queued_before_methods_merges() {
591 let db = database();
592 db.execute("DELETE FROM queue_entries", []).unwrap();
593 assert!(exists(&db, ENQUEUE, params!["first", "repo", "pull", 1, "{}", 0, "later", 0, "squash", "Ship it", "All of it"]));
594 let (method, title, body): (String, String, String) = db
595 .query_row("SELECT merge_method, commit_title, commit_message FROM queue_entries", [], |row| {
596 Ok((row.get(0)?, row.get(1)?, row.get(2)?))
597 })
598 .unwrap();
599 assert_eq!((method.as_str(), title.as_str(), body.as_str()), ("squash", "Ship it", "All of it"));
600 let row = |method: serde_json::Value| -> super::EntryRow {
601 serde_json::from_value(serde_json::json!({
602 "id": "e", "repo_id": "r", "pull_id": "p", "number": 1, "state": "waiting", "enqueued_by": "{}",
603 "keep_item_open": 0, "head_commit": null, "base_commit": null, "ahead": null, "combined_commit": null,
604 "results": null, "error": null, "tested_at": null, "created_at": "now", "finished_at": null,
605 "merge_method": method,
606 }))
607 .unwrap()
608 };
609 use g1t_contracts::rules::MergeMethod;
610 assert_eq!(row("rebase".into()).method(), MergeMethod::Rebase);
611 assert_eq!(row("fast_forward".into()).method(), MergeMethod::FastForward);
612 // Its states were built with merge commits then.
613 assert_eq!(row(serde_json::Value::Null).method(), MergeMethod::Merge);
614}
615
616#[test]
617fn a_clash_names_the_nearest_entry_ahead_that_changed_the_same_file() {
618 let item = |number: u32| g1t_contracts::work::QueueStackItem {
619 number,
620 title: String::new(),
621 source: g1t_contracts::repos::RepoPath { namespace: "n".into(), name: "r".into() },
622 branch: "b".into(),
623 commit: "c".into(),
624 method: g1t_contracts::rules::MergeMethod::Squash,
625 message: None,
626 author: None,
627 };
628 let ahead = vec![
629 (item(1), vec!["a.rs".to_owned(), "b.rs".to_owned()]),
630 (item(2), vec!["b.rs".to_owned()]),
631 (item(3), vec!["c.rs".to_owned()]),
632 ];
633 assert_eq!(super::clashed_with(&ahead, &["b.rs".to_owned()]), Some(2));
634 assert_eq!(super::clashed_with(&ahead, &["a.rs".to_owned()]), Some(1));
635 // With the default branch itself.
636 assert_eq!(super::clashed_with(&ahead, &["d.rs".to_owned()]), None);
637}