1use std::collections::BTreeMap;
10use std::fmt::{self, Debug};
11use std::iter;
12use std::slice;
13
14use git_workarea::{CommitId, GitContext, GitError, Identity, WorkAreaError};
15use log::{debug, error};
16use rayon::prelude::*;
17use thiserror::Error;
18
19use crate::check::{BranchCheck, Check, CheckResult, TopicCheck};
20use crate::commit::{Commit, CommitError, Topic};
21use crate::context::CheckGitContext;
22
23#[derive(Debug, Error)]
25#[non_exhaustive]
26pub enum RunError {
27 #[error("git error: {}", source)]
29 Git {
30 #[from]
32 source: GitError,
33 },
34 #[error("git workarea error: {}", source)]
36 WorkArea {
37 #[from]
39 source: WorkAreaError,
40 },
41 #[error("commit error: {}", source)]
43 Commit {
44 #[from]
46 source: CommitError,
47 },
48 #[error("run check error: failed to update the {} ref: {}", base_ref, output)]
50 UpdateRef {
51 base_ref: CommitId,
53 output: String,
55 },
56 #[error(
58 "run check error: failed to list refs from {} to {}",
59 base_ref,
60 new_ref
61 )]
62 RevList {
63 base_ref: CommitId,
65 new_ref: CommitId,
67 output: String,
69 },
70}
71
72impl RunError {
73 fn update_ref(base_ref: CommitId, output: &[u8]) -> Self {
74 RunError::UpdateRef {
75 base_ref,
76 output: String::from_utf8_lossy(output).into(),
77 }
78 }
79
80 fn rev_list(base_ref: CommitId, new_ref: CommitId, output: &[u8]) -> Self {
81 RunError::RevList {
82 base_ref,
83 new_ref,
84 output: String::from_utf8_lossy(output).into(),
85 }
86 }
87}
88
89#[derive(Default, Clone)]
91pub struct GitCheckConfiguration<'a> {
92 checks: Vec<&'a dyn Check>,
94 checks_branch: Vec<&'a dyn BranchCheck>,
96 checks_topic: Vec<&'a dyn TopicCheck>,
98 configuration: BTreeMap<String, String>,
100}
101
102#[derive(Debug)]
104pub struct TopicCheckResult {
105 commit_results: Vec<(CommitId, CheckResult)>,
107 topic_result: CheckResult,
109}
110
111impl TopicCheckResult {
112 pub fn commit_results(&self) -> slice::Iter<'_, (CommitId, CheckResult)> {
114 self.commit_results.iter()
115 }
116
117 pub fn topic_result(&self) -> &CheckResult {
119 &self.topic_result
120 }
121}
122
123impl From<TopicCheckResult> for CheckResult {
124 fn from(res: TopicCheckResult) -> Self {
125 res.commit_results
126 .into_iter()
127 .map(|(_, result)| result)
128 .chain(iter::once(res.topic_result))
129 .fold(Self::new(), Self::combine)
130 }
131}
132
133impl<'a> GitCheckConfiguration<'a> {
134 pub fn new() -> Self {
136 GitCheckConfiguration {
137 checks: Vec::new(),
138 checks_branch: Vec::new(),
139 checks_topic: Vec::new(),
140 configuration: BTreeMap::new(),
141 }
142 }
143
144 pub fn add_check(&mut self, check: &'a dyn Check) -> &mut Self {
146 self.checks.push(check);
147
148 self
149 }
150
151 pub fn add_branch_check(&mut self, check: &'a dyn BranchCheck) -> &mut Self {
153 self.checks_branch.push(check);
154
155 self
156 }
157
158 pub fn add_topic_check(&mut self, check: &'a dyn TopicCheck) -> &mut Self {
160 self.checks_topic.push(check);
161
162 self
163 }
164
165 pub fn add_configuration<K, V>(&mut self, key: K, value: V) -> &mut Self
170 where
171 K: Into<String>,
172 V: Into<String>,
173 {
174 self.configuration.insert(key.into(), value.into());
175
176 self
177 }
178
179 pub fn add_configurations<I, K, V>(&mut self, iter: I) -> &mut Self
184 where
185 I: IntoIterator<Item = (K, V)>,
186 K: Into<String>,
187 V: Into<String>,
188 {
189 self.configuration
190 .extend(iter.into_iter().map(|(k, v)| (k.into(), v.into())));
191
192 self
193 }
194
195 fn list<'b, B>(
197 &self,
198 ctx: &GitContext,
199 reason: &str,
200 target_branch: &CommitId,
201 bases: B,
202 topic: &CommitId,
203 ) -> Result<Vec<CommitId>, RunError>
204 where
205 B: IntoIterator<Item = &'b CommitId>,
206 {
207 let (new_ref, base_ref) = ctx.reserve_refs(format!("check/{}", reason), topic)?;
208 let update_ref = ctx
209 .git()
210 .arg("update-ref")
211 .args(["-m", reason])
212 .arg(&base_ref)
213 .arg(target_branch.as_str())
214 .output()
215 .map_err(|err| GitError::subcommand("update-ref", err))?;
216 if !update_ref.status.success() {
217 return Err(RunError::update_ref(
218 CommitId::new(base_ref),
219 &update_ref.stderr,
220 ));
221 }
222
223 let rev_list = ctx
224 .git()
225 .arg("rev-list")
226 .arg("--reverse")
227 .arg("--topo-order")
228 .arg(&new_ref)
229 .arg(format!("^{}", base_ref))
230 .args(bases.into_iter().map(|base| format!("^{}", base)))
231 .output()
232 .map_err(|err| GitError::subcommand("rev-list", err))?;
233 if !rev_list.status.success() {
234 return Err(RunError::rev_list(
235 CommitId::new(base_ref),
236 CommitId::new(new_ref),
237 &rev_list.stderr,
238 ));
239 }
240 let refs = String::from_utf8_lossy(&rev_list.stdout);
241
242 Ok(refs.lines().map(CommitId::new).collect())
243 }
244
245 fn run_check(ctx: &CheckGitContext, check: &dyn Check, commit: &Commit) -> CheckResult {
247 debug!(
248 target: "git-checks",
249 "running check {} on commit {}",
250 check.name(),
251 commit.sha1,
252 );
253
254 check.check(ctx, commit).unwrap_or_else(|err| {
255 error!(
256 target: "git-checks",
257 "check {} failed on commit {}: {:?}",
258 check.name(),
259 commit.sha1,
260 err,
261 );
262
263 let mut res = CheckResult::new();
264 res.add_alert(
265 format!(
266 "failed to run the {} check on commit {}",
267 check.name(),
268 commit.sha1,
269 ),
270 true,
271 );
272 res
273 })
274 }
275
276 fn run_branch_check(
278 ctx: &CheckGitContext,
279 check: &dyn BranchCheck,
280 commit: &CommitId,
281 ) -> CheckResult {
282 debug!(target: "git-checks", "running check {}", check.name());
283
284 check.check(ctx, commit).unwrap_or_else(|err| {
285 error!(
286 target: "git-checks",
287 "branch check {}: {:?}",
288 check.name(),
289 err,
290 );
291
292 let mut res = CheckResult::new();
293 res.add_alert(
294 format!("failed to run the {} branch check", check.name()),
295 true,
296 );
297 res
298 })
299 }
300
301 fn run_topic_check(
303 ctx: &CheckGitContext,
304 check: &dyn TopicCheck,
305 topic: &Topic,
306 ) -> CheckResult {
307 debug!(target: "git-checks", "running check {}", check.name());
308
309 check.check(ctx, topic).unwrap_or_else(|err| {
310 error!(
311 target: "git-checks",
312 "topic check {}: {:?}",
313 check.name(),
314 err,
315 );
316
317 let mut res = CheckResult::new();
318 res.add_alert(
319 format!("failed to run the {} topic check", check.name()),
320 true,
321 );
322 res
323 })
324 }
325
326 fn run_topic_impl(
328 &self,
329 ctx: &GitContext,
330 base: &CommitId,
331 refs: Vec<CommitId>,
332 owner: &Identity,
333 ) -> Result<TopicCheckResult, RunError> {
334 let topic_result = refs.last().map_or_else(
335 || Ok(CheckResult::new()) as Result<_, RunError>,
336 |head_commit| {
337 if self.checks_branch.is_empty() && self.checks_topic.is_empty() {
339 return Ok(CheckResult::new());
340 }
341
342 let workarea = ctx.prepare(head_commit)?;
343 let check_ctx = {
344 let mut ctx = CheckGitContext::new(workarea, owner.clone());
345 ctx.add_configurations(self.configuration.iter());
346 ctx
347 };
348 let topic = Topic::new(ctx, base, head_commit)?;
349
350 Ok(self
351 .checks_branch
352 .par_iter()
353 .map(|&check| Self::run_branch_check(&check_ctx, check, head_commit))
354 .chain(
355 self.checks_topic
356 .par_iter()
357 .map(|&check| Self::run_topic_check(&check_ctx, check, &topic)),
358 )
359 .reduce(CheckResult::new, CheckResult::combine))
360 },
361 )?;
362 let commit_results = refs
363 .into_par_iter()
364 .map(|sha1| {
365 self.run_commit(ctx, &sha1, owner)
366 .map(|result| (sha1, result))
367 })
368 .collect::<Vec<Result<_, RunError>>>()
369 .into_iter()
370 .collect::<Result<Vec<_>, RunError>>()?;
371
372 Ok(TopicCheckResult {
373 commit_results,
374 topic_result,
375 })
376 }
377
378 pub fn run_commit(
380 &self,
381 ctx: &GitContext,
382 commit: &CommitId,
383 owner: &Identity,
384 ) -> Result<CheckResult, RunError> {
385 if self.checks.is_empty() {
387 return Ok(CheckResult::new());
388 }
389
390 let workarea = ctx.prepare(commit)?;
391 let check_ctx = {
392 let mut ctx = CheckGitContext::new(workarea, owner.clone());
393 ctx.add_configurations(self.configuration.iter());
394 ctx
395 };
396
397 let commit = Commit::new(ctx, commit)?;
398
399 Ok(self
400 .checks
401 .par_iter()
402 .map(|&check| Self::run_check(&check_ctx, check, &commit))
403 .reduce(CheckResult::new, CheckResult::combine))
404 }
405
406 pub fn run_topic<R>(
408 &self,
409 ctx: &GitContext,
410 reason: R,
411 target_branch: &CommitId,
412 topic: &CommitId,
413 owner: &Identity,
414 ) -> Result<TopicCheckResult, RunError>
415 where
416 R: AsRef<str>,
417 {
418 let refs = self.list(ctx, reason.as_ref(), target_branch, &[], topic)?;
419 self.run_topic_impl(ctx, target_branch, refs, owner)
420 }
421
422 pub fn run_topic_multi_base<'b, R, B>(
424 &self,
425 ctx: &GitContext,
426 reason: R,
427 target_branch: &CommitId,
428 bases: B,
429 topic: &CommitId,
430 owner: &Identity,
431 ) -> Result<TopicCheckResult, RunError>
432 where
433 R: AsRef<str>,
434 B: IntoIterator<Item = &'b CommitId>,
435 {
436 let refs = self.list(ctx, reason.as_ref(), target_branch, bases, topic)?;
437 self.run_topic_impl(ctx, target_branch, refs, owner)
438 }
439}
440
441impl Debug for GitCheckConfiguration<'_> {
442 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
443 write!(
444 f,
445 "GitCheckConfiguration {{ {} commit checks, {} branch checks, {} topic checks }}",
446 self.checks.len(),
447 self.checks_branch.len(),
448 self.checks_topic.len(),
449 )
450 }
451}
452
453#[cfg(test)]
454mod test {
455 use std::iter;
456 use std::path::Path;
457
458 use git_workarea::{CommitId, GitContext, Identity};
459
460 use crate::run::{CheckResult, GitCheckConfiguration, TopicCheckResult};
461
462 mod checks {
463 use thiserror::Error;
464
465 use crate::impl_prelude::*;
466
467 #[derive(Debug)]
468 pub struct FailingCheck {}
469
470 #[derive(Debug, Error)]
471 #[error("the failing check did its thing")]
472 struct FailingCheckError;
473
474 impl Check for FailingCheck {
475 fn name(&self) -> &str {
476 "test-failing-check-commit"
477 }
478
479 fn check(
480 &self,
481 _: &CheckGitContext,
482 _: &Commit,
483 ) -> Result<CheckResult, Box<dyn Error>> {
484 Err(FailingCheckError.into())
485 }
486 }
487
488 impl BranchCheck for FailingCheck {
489 fn name(&self) -> &str {
490 "test-failing-check-branch"
491 }
492
493 fn check(
494 &self,
495 _: &CheckGitContext,
496 _: &CommitId,
497 ) -> Result<CheckResult, Box<dyn Error>> {
498 Err(FailingCheckError.into())
499 }
500 }
501
502 impl TopicCheck for FailingCheck {
503 fn name(&self) -> &str {
504 "test-failing-check-topic"
505 }
506
507 fn check(&self, _: &CheckGitContext, _: &Topic) -> Result<CheckResult, Box<dyn Error>> {
508 Err(FailingCheckError.into())
509 }
510 }
511 }
512
513 #[test]
514 fn test_configuration_debug() {
515 let config = GitCheckConfiguration::new();
516 assert_eq!(
517 format!("{:?}", config),
518 "GitCheckConfiguration { 0 commit checks, 0 branch checks, 0 topic checks }",
519 );
520 }
521
522 const TARGET_COMMIT: &str = "27ff3ef5532d76afa046f76f4dd8f588dc3e83c3";
523 const TOPIC_COMMIT: &str = "a61fd3759b61a4a1f740f3fe656bc42151cefbdd";
524 const TOPIC2_COMMIT: &str = "112e9b34401724bff57f68cf47c5065d4342b263";
525
526 fn git_context() -> GitContext {
527 let gitdir = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/../.git"));
528 if !gitdir.exists() {
529 panic!("The tests must be run from a git checkout.");
530 }
531
532 GitContext::new(gitdir)
533 }
534
535 fn run_commit(config: &GitCheckConfiguration) -> CheckResult {
536 let ctx = git_context();
537 config
538 .run_commit(
539 &ctx,
540 &CommitId::new(TOPIC_COMMIT),
541 &Identity::new(
542 "Rust Git Checks Core Tests",
543 "rust-git-checks@example.invalid",
544 ),
545 )
546 .unwrap()
547 }
548
549 fn run_topic(
550 test_name: &str,
551 config: &GitCheckConfiguration,
552 commit: &str,
553 ) -> TopicCheckResult {
554 let ctx = git_context();
555 config
556 .run_topic(
557 &ctx,
558 test_name,
559 &CommitId::new(TARGET_COMMIT),
560 &CommitId::new(commit),
561 &Identity::new(
562 "Rust Git Checks Core Tests",
563 "rust-git-checks@example.invalid",
564 ),
565 )
566 .unwrap()
567 }
568
569 fn run_topic_bases(
570 test_name: &str,
571 config: &GitCheckConfiguration,
572 commit: &str,
573 bases: &[&str],
574 ) -> TopicCheckResult {
575 let ctx = git_context();
576 let bases: Vec<_> = bases.iter().copied().map(CommitId::new).collect();
577 config
578 .run_topic_multi_base(
579 &ctx,
580 test_name,
581 &CommitId::new(TARGET_COMMIT),
582 bases.iter(),
583 &CommitId::new(commit),
584 &Identity::new(
585 "Rust Git Checks Core Tests",
586 "rust-git-checks@example.invalid",
587 ),
588 )
589 .unwrap()
590 }
591
592 fn no_strings<'a>() -> iter::Empty<&'a String> {
593 iter::empty()
594 }
595
596 fn check_result(result: &CheckResult, errors: &[&str]) {
597 itertools::assert_equal(result.warnings(), no_strings());
598 itertools::assert_equal(result.alerts(), errors);
599 itertools::assert_equal(result.errors(), no_strings());
600 assert!(!result.temporary());
601 assert!(!result.allowed());
602 assert!(!result.pass());
603 }
604
605 fn check_result_ok(result: &CheckResult) {
606 itertools::assert_equal(result.warnings(), no_strings());
607 itertools::assert_equal(result.alerts(), no_strings());
608 itertools::assert_equal(result.errors(), no_strings());
609 assert!(!result.temporary());
610 assert!(!result.allowed());
611 assert!(result.pass());
612 }
613
614 #[test]
615 fn test_commit_check_errors_commit() {
616 let check = self::checks::FailingCheck {};
617 let mut config = GitCheckConfiguration::new();
618 config.add_check(&check);
619
620 let result = run_commit(&config);
621
622 check_result(
623 &result,
624 &[
625 "failed to run the test-failing-check-commit check on commit \
626 a61fd3759b61a4a1f740f3fe656bc42151cefbdd",
627 ],
628 );
629 }
630
631 #[test]
632 fn test_branch_check_errors_commit() {
633 let check = self::checks::FailingCheck {};
634 let mut config = GitCheckConfiguration::new();
635 config.add_branch_check(&check);
636
637 let result = run_commit(&config);
638
639 check_result_ok(&result);
640 }
641
642 #[test]
643 fn test_topic_check_errors_commit() {
644 let check = self::checks::FailingCheck {};
645 let mut config = GitCheckConfiguration::new();
646 config.add_topic_check(&check);
647
648 let result = run_commit(&config);
649
650 check_result_ok(&result);
651 }
652
653 #[test]
654 fn test_commit_check_errors_topic() {
655 let check = self::checks::FailingCheck {};
656 let mut config = GitCheckConfiguration::new();
657 config.add_check(&check);
658
659 let result = run_topic("test_commit_check_errors_topic", &config, TOPIC_COMMIT);
660
661 let mut commit_results = result.commit_results();
662 let (commit, commit_result) = commit_results.next().unwrap();
663 assert_eq!(commit.as_str(), "a61fd3759b61a4a1f740f3fe656bc42151cefbdd");
664 check_result(
665 commit_result,
666 &[
667 "failed to run the test-failing-check-commit check on commit \
668 a61fd3759b61a4a1f740f3fe656bc42151cefbdd",
669 ],
670 );
671 if let Some(res) = commit_results.next() {
672 panic!(
673 "multiple commits returned from a single-commit topic: {:?}",
674 res,
675 );
676 }
677 check_result_ok(result.topic_result());
678 check_result(
679 &result.into(),
680 &[
681 "failed to run the test-failing-check-commit check on commit \
682 a61fd3759b61a4a1f740f3fe656bc42151cefbdd",
683 ],
684 );
685 }
686
687 #[test]
688 fn test_commit_check_errors_topic_bases() {
689 let check = self::checks::FailingCheck {};
690 let mut config = GitCheckConfiguration::new();
691 config.add_check(&check);
692
693 let result = run_topic_bases(
694 "test_commit_check_errors_topic_bases",
695 &config,
696 TOPIC2_COMMIT,
697 &[TOPIC_COMMIT],
698 );
699
700 let mut commit_results = result.commit_results();
701 let (commit, commit_result) = commit_results.next().unwrap();
702 assert_eq!(commit.as_str(), "112e9b34401724bff57f68cf47c5065d4342b263");
703 check_result(
704 commit_result,
705 &[
706 "failed to run the test-failing-check-commit check on commit \
707 112e9b34401724bff57f68cf47c5065d4342b263",
708 ],
709 );
710 if let Some(res) = commit_results.next() {
711 panic!(
712 "multiple commits returned from a single-commit topic: {:?}",
713 res,
714 );
715 }
716 check_result_ok(result.topic_result());
717 check_result(
718 &result.into(),
719 &[
720 "failed to run the test-failing-check-commit check on commit \
721 112e9b34401724bff57f68cf47c5065d4342b263",
722 ],
723 );
724 }
725
726 #[test]
727 fn test_branch_check_errors_topic() {
728 let check = self::checks::FailingCheck {};
729 let mut config = GitCheckConfiguration::new();
730 config.add_branch_check(&check);
731
732 let result = run_topic("test_branch_check_errors_topic", &config, TOPIC_COMMIT);
733
734 let mut commit_results = result.commit_results();
735 let (commit, commit_result) = commit_results.next().unwrap();
736 assert_eq!(commit.as_str(), "a61fd3759b61a4a1f740f3fe656bc42151cefbdd");
737 check_result_ok(commit_result);
738 if let Some(res) = commit_results.next() {
739 panic!(
740 "multiple commits returned from a single-commit topic: {:?}",
741 res,
742 );
743 }
744 check_result(
745 result.topic_result(),
746 &["failed to run the test-failing-check-branch branch check"],
747 );
748 check_result(
749 &result.into(),
750 &["failed to run the test-failing-check-branch branch check"],
751 );
752 }
753
754 #[test]
755 fn test_branch_check_errors_topic_bases() {
756 let check = self::checks::FailingCheck {};
757 let mut config = GitCheckConfiguration::new();
758 config.add_branch_check(&check);
759
760 let result = run_topic_bases(
761 "test_branch_check_errors_topic_bases",
762 &config,
763 TOPIC2_COMMIT,
764 &[TOPIC_COMMIT],
765 );
766
767 let mut commit_results = result.commit_results();
768 let (commit, commit_result) = commit_results.next().unwrap();
769 assert_eq!(commit.as_str(), "112e9b34401724bff57f68cf47c5065d4342b263");
770 check_result_ok(commit_result);
771 if let Some(res) = commit_results.next() {
772 panic!(
773 "multiple commits returned from a single-commit topic: {:?}",
774 res,
775 );
776 }
777 check_result(
778 result.topic_result(),
779 &["failed to run the test-failing-check-branch branch check"],
780 );
781 check_result(
782 &result.into(),
783 &["failed to run the test-failing-check-branch branch check"],
784 );
785 }
786
787 #[test]
788 fn test_topic_check_errors_topic() {
789 let check = self::checks::FailingCheck {};
790 let mut config = GitCheckConfiguration::new();
791 config.add_topic_check(&check);
792
793 let result = run_topic("test_topic_check_errors_topic", &config, TOPIC_COMMIT);
794
795 let mut commit_results = result.commit_results();
796 let (commit, commit_result) = commit_results.next().unwrap();
797 assert_eq!(commit.as_str(), "a61fd3759b61a4a1f740f3fe656bc42151cefbdd");
798 check_result_ok(commit_result);
799 if let Some(res) = commit_results.next() {
800 panic!(
801 "multiple commits returned from a single-commit topic: {:?}",
802 res,
803 );
804 }
805 check_result(
806 result.topic_result(),
807 &["failed to run the test-failing-check-topic topic check"],
808 );
809 check_result(
810 &result.into(),
811 &["failed to run the test-failing-check-topic topic check"],
812 );
813 }
814
815 #[test]
816 fn test_topic_check_errors_topic_bases() {
817 let check = self::checks::FailingCheck {};
818 let mut config = GitCheckConfiguration::new();
819 config.add_topic_check(&check);
820
821 let result = run_topic_bases(
822 "test_topic_check_errors_topic_bases",
823 &config,
824 TOPIC2_COMMIT,
825 &[TOPIC_COMMIT],
826 );
827
828 let mut commit_results = result.commit_results();
829 let (commit, commit_result) = commit_results.next().unwrap();
830 assert_eq!(commit.as_str(), "112e9b34401724bff57f68cf47c5065d4342b263");
831 check_result_ok(commit_result);
832 if let Some(res) = commit_results.next() {
833 panic!(
834 "multiple commits returned from a single-commit topic: {:?}",
835 res,
836 );
837 }
838 check_result(
839 result.topic_result(),
840 &["failed to run the test-failing-check-topic topic check"],
841 );
842 check_result(
843 &result.into(),
844 &["failed to run the test-failing-check-topic topic check"],
845 );
846 }
847
848 #[test]
849 fn test_multiple_check_commit() {
850 let check = self::checks::FailingCheck {};
851 let mut config = GitCheckConfiguration::new();
852 config.add_check(&check);
853 config.add_check(&check);
854 config.add_branch_check(&check);
855 config.add_branch_check(&check);
856 config.add_topic_check(&check);
857 config.add_topic_check(&check);
858
859 let result = run_commit(&config);
860
861 check_result(
862 &result,
863 &[
864 "failed to run the test-failing-check-commit check on commit \
865 a61fd3759b61a4a1f740f3fe656bc42151cefbdd",
866 "failed to run the test-failing-check-commit check on commit \
867 a61fd3759b61a4a1f740f3fe656bc42151cefbdd",
868 ],
869 );
870 }
871
872 #[test]
873 fn test_multiple_check_topic() {
874 let check = self::checks::FailingCheck {};
875 let mut config = GitCheckConfiguration::new();
876 config.add_check(&check);
877 config.add_check(&check);
878 config.add_branch_check(&check);
879 config.add_branch_check(&check);
880 config.add_topic_check(&check);
881 config.add_topic_check(&check);
882
883 let result = run_topic("test_multiple_check_topic", &config, TOPIC_COMMIT);
884
885 let mut commit_results = result.commit_results();
886 let (commit, commit_result) = commit_results.next().unwrap();
887 assert_eq!(commit.as_str(), "a61fd3759b61a4a1f740f3fe656bc42151cefbdd");
888 check_result(
889 commit_result,
890 &[
891 "failed to run the test-failing-check-commit check on commit \
892 a61fd3759b61a4a1f740f3fe656bc42151cefbdd",
893 "failed to run the test-failing-check-commit check on commit \
894 a61fd3759b61a4a1f740f3fe656bc42151cefbdd",
895 ],
896 );
897 if let Some(res) = commit_results.next() {
898 panic!(
899 "multiple commits returned from a single-commit topic: {:?}",
900 res,
901 );
902 }
903 check_result(
904 result.topic_result(),
905 &[
906 "failed to run the test-failing-check-branch branch check",
907 "failed to run the test-failing-check-branch branch check",
908 "failed to run the test-failing-check-topic topic check",
909 "failed to run the test-failing-check-topic topic check",
910 ],
911 );
912 check_result(
913 &result.into(),
914 &[
915 "failed to run the test-failing-check-commit check on commit \
916 a61fd3759b61a4a1f740f3fe656bc42151cefbdd",
917 "failed to run the test-failing-check-commit check on commit \
918 a61fd3759b61a4a1f740f3fe656bc42151cefbdd",
919 "failed to run the test-failing-check-branch branch check",
920 "failed to run the test-failing-check-branch branch check",
921 "failed to run the test-failing-check-topic topic check",
922 "failed to run the test-failing-check-topic topic check",
923 ],
924 );
925 }
926
927 #[test]
928 fn test_check_multiple_topic() {
929 let check = self::checks::FailingCheck {};
930 let mut config = GitCheckConfiguration::new();
931 config.add_check(&check);
932 config.add_branch_check(&check);
933 config.add_topic_check(&check);
934
935 let result = run_topic("test_check_multiple_topic", &config, TOPIC2_COMMIT);
936
937 let mut commit_results = result.commit_results();
938 let (commit, commit_result) = commit_results.next().unwrap();
939 assert_eq!(commit.as_str(), "a61fd3759b61a4a1f740f3fe656bc42151cefbdd");
940 check_result(
941 commit_result,
942 &[
943 "failed to run the test-failing-check-commit check on commit \
944 a61fd3759b61a4a1f740f3fe656bc42151cefbdd",
945 ],
946 );
947 let (commit, commit_result) = commit_results.next().unwrap();
948 assert_eq!(commit.as_str(), "112e9b34401724bff57f68cf47c5065d4342b263");
949 check_result(
950 commit_result,
951 &[
952 "failed to run the test-failing-check-commit check on commit \
953 112e9b34401724bff57f68cf47c5065d4342b263",
954 ],
955 );
956 if let Some(res) = commit_results.next() {
957 panic!(
958 "multiple commits returned from a single-commit topic: {:?}",
959 res,
960 );
961 }
962 check_result(
963 result.topic_result(),
964 &[
965 "failed to run the test-failing-check-branch branch check",
966 "failed to run the test-failing-check-topic topic check",
967 ],
968 );
969 check_result(
970 &result.into(),
971 &[
972 "failed to run the test-failing-check-commit check on commit \
973 a61fd3759b61a4a1f740f3fe656bc42151cefbdd",
974 "failed to run the test-failing-check-commit check on commit \
975 112e9b34401724bff57f68cf47c5065d4342b263",
976 "failed to run the test-failing-check-branch branch check",
977 "failed to run the test-failing-check-topic topic check",
978 ],
979 );
980 }
981
982 #[test]
983 fn test_check_configuration_overwrite() {
984 let config = {
985 let mut config = GitCheckConfiguration::new();
986 config
987 .add_configuration("key1", "value1")
988 .add_configuration("key2", "value2")
989 .add_configuration("key3", "value3")
990 .add_configuration("key1", "value1_overwrite")
991 .add_configurations([("key2", "value2_overwrite"), ("key4", "value4")]);
992 config
993 };
994
995 let expect = [
996 ("key1", Some("value1_overwrite")),
997 ("key2", Some("value2_overwrite")),
998 ("key3", Some("value3")),
999 ("key4", Some("value4")),
1000 ("key5", None),
1001 ];
1002
1003 for (k, v) in expect {
1004 assert_eq!(config.configuration.get(k).map(|v| v.as_str()), v);
1005 }
1006 assert_eq!(config.configuration.len(), 4);
1007 }
1008}