1use std::collections::HashMap;
42use std::sync::Arc;
43use std::time::{Duration, Instant};
44
45use crate::brick::{
46 Brick, BrickBudget, BrickError, BrickPhase, BrickResult, BrickVerification, BudgetViolation,
47};
48
49#[derive(Debug)]
56pub struct BrickHouse {
57 name: String,
59 budget: BrickBudget,
61 bricks: Vec<BrickEntry>,
63 last_report: Option<BudgetReport>,
65}
66
67struct BrickEntry {
69 brick: Arc<dyn Brick>,
71 allocated_ms: u32,
73 last_render_time: Option<Duration>,
75}
76
77impl std::fmt::Debug for BrickEntry {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 f.debug_struct("BrickEntry")
80 .field("brick_name", &self.brick.brick_name())
81 .field("allocated_ms", &self.allocated_ms)
82 .field("last_render_time", &self.last_render_time)
83 .finish()
84 }
85}
86
87#[derive(Debug, Clone)]
89pub struct BudgetReport {
90 pub house_name: String,
92 pub total_budget_ms: u32,
94 pub total_used_ms: u32,
96 pub brick_timings: HashMap<String, BrickTiming>,
98 pub violations: Vec<BudgetViolation>,
100 pub timestamp: std::time::SystemTime,
102}
103
104#[derive(Debug, Clone)]
106pub struct BrickTiming {
107 pub name: String,
109 pub budget_ms: u32,
111 pub used_ms: u32,
113 pub exceeded: bool,
115}
116
117impl BudgetReport {
118 #[must_use]
120 pub fn within_budget(&self) -> bool {
121 self.violations.is_empty() && self.total_used_ms <= self.total_budget_ms
122 }
123
124 #[must_use]
126 pub fn utilization(&self) -> f32 {
127 if self.total_budget_ms == 0 {
128 0.0
129 } else {
130 (self.total_used_ms as f32 / self.total_budget_ms as f32) * 100.0
131 }
132 }
133
134 #[must_use]
136 pub fn violations(&self) -> &[BudgetViolation] {
137 &self.violations
138 }
139}
140
141impl BrickHouse {
142 #[must_use]
144 pub fn new(name: impl Into<String>, budget_ms: u32) -> Self {
145 Self {
146 name: name.into(),
147 budget: BrickBudget::uniform(budget_ms),
148 bricks: Vec::new(),
149 last_report: None,
150 }
151 }
152
153 pub fn add_brick(&mut self, brick: Arc<dyn Brick>, budget_ms: u32) -> BrickResult<()> {
159 let current_total: u32 = self.bricks.iter().map(|b| b.allocated_ms).sum();
160 let new_total = current_total + budget_ms;
161
162 if new_total > self.budget.total_ms {
163 return Err(BrickError::BudgetExceeded(BudgetViolation {
164 brick_name: brick.brick_name().to_string(),
165 budget: self.budget,
166 actual: Duration::from_millis(new_total as u64),
167 phase: None,
168 }));
169 }
170
171 self.bricks.push(BrickEntry {
172 brick,
173 allocated_ms: budget_ms,
174 last_render_time: None,
175 });
176
177 Ok(())
178 }
179
180 #[must_use]
182 pub fn name(&self) -> &str {
183 &self.name
184 }
185
186 #[must_use]
188 pub fn budget(&self) -> BrickBudget {
189 self.budget
190 }
191
192 #[must_use]
194 pub fn brick_count(&self) -> usize {
195 self.bricks.len()
196 }
197
198 #[must_use]
200 pub fn remaining_budget_ms(&self) -> u32 {
201 let allocated: u32 = self.bricks.iter().map(|b| b.allocated_ms).sum();
202 self.budget.total_ms.saturating_sub(allocated)
203 }
204
205 pub fn verify_all(&self) -> Vec<(&str, BrickVerification)> {
209 self.bricks
210 .iter()
211 .map(|entry| {
212 let name = entry.brick.brick_name();
213 let verification = entry.brick.verify();
214 (name, verification)
215 })
216 .collect()
217 }
218
219 #[must_use]
221 pub fn can_render(&self) -> bool {
222 self.bricks.iter().all(|entry| entry.brick.can_render())
223 }
224
225 pub fn render(&mut self) -> BrickResult<String> {
233 let mut html_parts = Vec::new();
234 let mut timings = HashMap::new();
235 let mut violations = Vec::new();
236 let mut total_used_ms = 0u32;
237
238 for entry in &mut self.bricks {
239 let start = Instant::now();
240
241 let verification = entry.brick.verify();
243 if !verification.is_valid() {
244 let (assertion, reason) = verification
245 .failed
246 .first()
247 .map(|(a, r)| (a.clone(), r.clone()))
248 .unwrap_or_else(|| {
249 (crate::brick::BrickAssertion::TextVisible, "Unknown".into())
250 });
251 return Err(BrickError::AssertionFailed { assertion, reason });
252 }
253
254 let html = entry.brick.to_html();
256 html_parts.push(html);
257
258 let elapsed = start.elapsed();
259 let elapsed_ms = elapsed.as_millis() as u32;
260 entry.last_render_time = Some(elapsed);
261 total_used_ms += elapsed_ms;
262
263 let exceeded = elapsed_ms > entry.allocated_ms;
264 let brick_name = entry.brick.brick_name().to_string();
265
266 timings.insert(
267 brick_name.clone(),
268 BrickTiming {
269 name: brick_name.clone(),
270 budget_ms: entry.allocated_ms,
271 used_ms: elapsed_ms,
272 exceeded,
273 },
274 );
275
276 if exceeded {
277 violations.push(BudgetViolation {
278 brick_name,
279 budget: BrickBudget::uniform(entry.allocated_ms),
280 actual: elapsed,
281 phase: Some(BrickPhase::Paint),
282 });
283 }
284 }
285
286 self.last_report = Some(BudgetReport {
288 house_name: self.name.clone(),
289 total_budget_ms: self.budget.total_ms,
290 total_used_ms,
291 brick_timings: timings,
292 violations: violations.clone(),
293 timestamp: std::time::SystemTime::now(),
294 });
295
296 if !violations.is_empty() {
298 return Err(BrickError::BudgetExceeded(
299 violations.into_iter().next().expect("violations not empty"),
300 ));
301 }
302
303 Ok(html_parts.join("\n"))
304 }
305
306 #[must_use]
308 pub fn last_report(&self) -> Option<&BudgetReport> {
309 self.last_report.as_ref()
310 }
311
312 #[must_use]
314 pub fn to_css(&self) -> String {
315 self.bricks
316 .iter()
317 .map(|entry| entry.brick.to_css())
318 .collect::<Vec<_>>()
319 .join("\n")
320 }
321}
322
323pub struct BrickHouseBuilder {
325 name: String,
326 budget_ms: u32,
327 bricks: Vec<(Arc<dyn Brick>, u32)>,
328}
329
330impl std::fmt::Debug for BrickHouseBuilder {
331 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
332 f.debug_struct("BrickHouseBuilder")
333 .field("name", &self.name)
334 .field("budget_ms", &self.budget_ms)
335 .field("brick_count", &self.bricks.len())
336 .finish()
337 }
338}
339
340impl BrickHouseBuilder {
341 #[must_use]
343 pub fn new(name: impl Into<String>) -> Self {
344 Self {
345 name: name.into(),
346 budget_ms: 1000, bricks: Vec::new(),
348 }
349 }
350
351 #[must_use]
353 pub fn budget_ms(mut self, ms: u32) -> Self {
354 self.budget_ms = ms;
355 self
356 }
357
358 #[must_use]
360 pub fn brick(mut self, brick: Arc<dyn Brick>, budget_ms: u32) -> Self {
361 self.bricks.push((brick, budget_ms));
362 self
363 }
364
365 pub fn build(self) -> BrickResult<BrickHouse> {
371 let total_brick_budget: u32 = self.bricks.iter().map(|(_, ms)| *ms).sum();
372
373 if total_brick_budget > self.budget_ms {
374 return Err(BrickError::BudgetExceeded(BudgetViolation {
375 brick_name: self.name.clone(),
376 budget: BrickBudget::uniform(self.budget_ms),
377 actual: Duration::from_millis(total_brick_budget as u64),
378 phase: None,
379 }));
380 }
381
382 let mut house = BrickHouse::new(self.name, self.budget_ms);
383 for (brick, budget) in self.bricks {
384 house.add_brick(brick, budget)?;
385 }
386
387 Ok(house)
388 }
389}
390
391#[derive(Debug, Clone)]
396pub struct JidokaAlert {
397 pub house_name: String,
399 pub brick_name: String,
401 pub budget_ms: u32,
403 pub actual_ms: u32,
405 pub phase: Option<BrickPhase>,
407 pub timestamp: std::time::SystemTime,
409 pub stack_trace: Option<String>,
411}
412
413impl JidokaAlert {
414 #[must_use]
416 pub fn from_violation(house_name: &str, violation: &BudgetViolation) -> Self {
417 Self {
418 house_name: house_name.to_string(),
419 brick_name: violation.brick_name.clone(),
420 budget_ms: violation.budget.total_ms,
421 actual_ms: violation.actual.as_millis() as u32,
422 phase: violation.phase,
423 timestamp: std::time::SystemTime::now(),
424 stack_trace: None,
425 }
426 }
427
428 #[must_use]
430 pub fn overage_percent(&self) -> f32 {
431 if self.budget_ms == 0 {
432 0.0
433 } else {
434 ((self.actual_ms as f32 / self.budget_ms as f32) - 1.0) * 100.0
435 }
436 }
437}
438
439#[cfg(test)]
440#[allow(clippy::unwrap_used, clippy::expect_used)]
441mod tests {
442 use super::*;
443 use crate::brick::BrickAssertion;
444
445 struct SimpleBrick {
446 name: &'static str,
447 }
448
449 impl Brick for SimpleBrick {
450 fn brick_name(&self) -> &'static str {
451 self.name
452 }
453
454 fn assertions(&self) -> &[BrickAssertion] {
455 &[]
456 }
457
458 fn budget(&self) -> BrickBudget {
459 BrickBudget::uniform(16)
460 }
461
462 fn verify(&self) -> BrickVerification {
463 BrickVerification {
464 passed: vec![],
465 failed: vec![],
466 verification_time: Duration::from_micros(1),
467 }
468 }
469
470 fn to_html(&self) -> String {
471 format!("<div class=\"{}\">{}</div>", self.name, self.name)
472 }
473
474 fn to_css(&self) -> String {
475 format!(".{} {{ display: block; }}", self.name)
476 }
477 }
478
479 #[test]
480 fn test_brick_house_creation() {
481 let house = BrickHouse::new("test-house", 1000);
482 assert_eq!(house.name(), "test-house");
483 assert_eq!(house.budget().total_ms, 1000);
484 assert_eq!(house.brick_count(), 0);
485 }
486
487 #[test]
488 fn test_brick_house_add_brick() {
489 let mut house = BrickHouse::new("test-house", 1000);
490 let brick = Arc::new(SimpleBrick { name: "test" });
491
492 house.add_brick(brick, 100).expect("should add brick");
493 assert_eq!(house.brick_count(), 1);
494 assert_eq!(house.remaining_budget_ms(), 900);
495 }
496
497 #[test]
498 fn test_brick_house_budget_exceeded() {
499 let mut house = BrickHouse::new("test-house", 100);
500 let brick1 = Arc::new(SimpleBrick { name: "brick1" });
501 let brick2 = Arc::new(SimpleBrick { name: "brick2" });
502
503 house.add_brick(brick1, 60).expect("should add first brick");
504 let result = house.add_brick(brick2, 60);
505
506 assert!(result.is_err());
507 }
508
509 #[test]
510 fn test_brick_house_builder() {
511 let brick1 = Arc::new(SimpleBrick { name: "status" });
512 let brick2 = Arc::new(SimpleBrick { name: "content" });
513
514 let house = BrickHouseBuilder::new("app")
515 .budget_ms(1000)
516 .brick(brick1, 100)
517 .brick(brick2, 200)
518 .build()
519 .expect("should build house");
520
521 assert_eq!(house.brick_count(), 2);
522 assert_eq!(house.remaining_budget_ms(), 700);
523 }
524
525 #[test]
526 fn test_brick_house_builder_exceeds_budget() {
527 let brick1 = Arc::new(SimpleBrick { name: "big" });
528 let brick2 = Arc::new(SimpleBrick { name: "bigger" });
529
530 let result = BrickHouseBuilder::new("app")
531 .budget_ms(100)
532 .brick(brick1, 60)
533 .brick(brick2, 60)
534 .build();
535
536 assert!(result.is_err());
537 }
538
539 #[test]
540 fn test_brick_house_render() {
541 let brick = Arc::new(SimpleBrick { name: "test" });
542 let mut house = BrickHouse::new("test-house", 1000);
543 house.add_brick(brick, 100).expect("should add brick");
544
545 let html = house.render().expect("should render");
546 assert!(html.contains("test"));
547 }
548
549 #[test]
550 fn test_jidoka_alert() {
551 let violation = BudgetViolation {
552 brick_name: "slow-brick".into(),
553 budget: BrickBudget::uniform(100),
554 actual: Duration::from_millis(150),
555 phase: Some(BrickPhase::Paint),
556 };
557
558 let alert = JidokaAlert::from_violation("app", &violation);
559 assert_eq!(alert.overage_percent(), 50.0);
560 }
561
562 #[test]
563 fn test_budget_report_within_budget() {
564 let report = BudgetReport {
565 house_name: "test".into(),
566 total_budget_ms: 1000,
567 total_used_ms: 500,
568 brick_timings: HashMap::new(),
569 violations: vec![],
570 timestamp: std::time::SystemTime::now(),
571 };
572 assert!(report.within_budget());
573 assert_eq!(report.utilization(), 50.0);
574 }
575
576 #[test]
577 fn test_budget_report_over_budget() {
578 let violation = BudgetViolation {
579 brick_name: "test".into(),
580 budget: BrickBudget::uniform(100),
581 actual: Duration::from_millis(150),
582 phase: None,
583 };
584 let report = BudgetReport {
585 house_name: "test".into(),
586 total_budget_ms: 1000,
587 total_used_ms: 1500,
588 brick_timings: HashMap::new(),
589 violations: vec![violation],
590 timestamp: std::time::SystemTime::now(),
591 };
592 assert!(!report.within_budget());
593 assert!(!report.violations().is_empty());
594 }
595
596 #[test]
597 fn test_budget_report_zero_budget() {
598 let report = BudgetReport {
599 house_name: "test".into(),
600 total_budget_ms: 0,
601 total_used_ms: 0,
602 brick_timings: HashMap::new(),
603 violations: vec![],
604 timestamp: std::time::SystemTime::now(),
605 };
606 assert_eq!(report.utilization(), 0.0);
607 }
608
609 #[test]
610 fn test_brick_timing() {
611 let timing = BrickTiming {
612 name: "test".into(),
613 budget_ms: 100,
614 used_ms: 50,
615 exceeded: false,
616 };
617 assert_eq!(timing.name, "test");
618 assert!(!timing.exceeded);
619 }
620
621 #[test]
622 fn test_brick_timing_exceeded() {
623 let timing = BrickTiming {
624 name: "slow".into(),
625 budget_ms: 100,
626 used_ms: 150,
627 exceeded: true,
628 };
629 assert!(timing.exceeded);
630 }
631
632 #[test]
633 fn test_brick_house_verify_all() {
634 let brick1 = Arc::new(SimpleBrick { name: "brick1" });
635 let brick2 = Arc::new(SimpleBrick { name: "brick2" });
636 let mut house = BrickHouse::new("test", 1000);
637 house.add_brick(brick1, 100).unwrap();
638 house.add_brick(brick2, 100).unwrap();
639
640 let verifications = house.verify_all();
641 assert_eq!(verifications.len(), 2);
642 }
643
644 #[test]
645 fn test_brick_house_can_render() {
646 let brick = Arc::new(SimpleBrick { name: "test" });
647 let mut house = BrickHouse::new("test", 1000);
648 house.add_brick(brick, 100).unwrap();
649
650 assert!(house.can_render());
651 }
652
653 #[test]
654 fn test_brick_entry_debug() {
655 let brick = Arc::new(SimpleBrick { name: "test" });
656 let entry = BrickEntry {
657 brick,
658 allocated_ms: 100,
659 last_render_time: Some(Duration::from_millis(50)),
660 };
661 let debug_str = format!("{:?}", entry);
662 assert!(debug_str.contains("test"));
663 assert!(debug_str.contains("100"));
664 }
665
666 #[test]
667 fn test_brick_house_to_css() {
668 let brick1 = Arc::new(SimpleBrick { name: "brick1" });
669 let brick2 = Arc::new(SimpleBrick { name: "brick2" });
670 let mut house = BrickHouse::new("test", 1000);
671 house.add_brick(brick1, 100).unwrap();
672 house.add_brick(brick2, 100).unwrap();
673
674 let css = house.to_css();
675 assert!(css.contains("brick1"));
676 assert!(css.contains("brick2"));
677 }
678
679 #[test]
680 fn test_brick_house_last_report_none() {
681 let house = BrickHouse::new("test", 1000);
682 assert!(house.last_report().is_none());
683 }
684
685 #[test]
686 fn test_brick_house_render_populates_report() {
687 let brick = Arc::new(SimpleBrick { name: "test" });
688 let mut house = BrickHouse::new("test-house", 1000);
689 house.add_brick(brick, 100).unwrap();
690
691 let _ = house.render().unwrap();
692
693 let report = house.last_report();
694 assert!(report.is_some());
695 let report = report.unwrap();
696 assert_eq!(report.house_name, "test-house");
697 assert!(report.brick_timings.contains_key("test"));
698 }
699
700 #[test]
701 fn test_budget_report_violations() {
702 let violation = BudgetViolation {
703 brick_name: "slow".into(),
704 budget: BrickBudget::uniform(100),
705 actual: Duration::from_millis(150),
706 phase: Some(BrickPhase::Paint),
707 };
708 let report = BudgetReport {
709 house_name: "test".into(),
710 total_budget_ms: 1000,
711 total_used_ms: 150,
712 brick_timings: HashMap::new(),
713 violations: vec![violation],
714 timestamp: std::time::SystemTime::now(),
715 };
716
717 assert!(!report.within_budget());
718 assert_eq!(report.violations().len(), 1);
719 }
720
721 #[test]
722 fn test_brick_house_builder_debug() {
723 let builder = BrickHouseBuilder::new("test-app").budget_ms(500);
724 let debug_str = format!("{:?}", builder);
725 assert!(debug_str.contains("test-app"));
726 assert!(debug_str.contains("500"));
727 }
728
729 #[test]
730 fn test_brick_house_debug() {
731 let mut house = BrickHouse::new("test-house", 1000);
732 let brick = Arc::new(SimpleBrick { name: "test" });
733 house.add_brick(brick, 100).unwrap();
734
735 let debug_str = format!("{:?}", house);
736 assert!(debug_str.contains("test-house"));
737 assert!(debug_str.contains("1000"));
738 }
739
740 #[test]
741 fn test_jidoka_alert_zero_budget() {
742 let violation = BudgetViolation {
743 brick_name: "test".into(),
744 budget: BrickBudget::uniform(0),
745 actual: Duration::from_millis(10),
746 phase: None,
747 };
748
749 let alert = JidokaAlert::from_violation("house", &violation);
750 assert_eq!(alert.overage_percent(), 0.0);
751 }
752
753 #[test]
754 fn test_jidoka_alert_fields() {
755 let violation = BudgetViolation {
756 brick_name: "slow-brick".into(),
757 budget: BrickBudget::uniform(100),
758 actual: Duration::from_millis(200),
759 phase: Some(BrickPhase::Layout),
760 };
761
762 let alert = JidokaAlert::from_violation("my-house", &violation);
763 assert_eq!(alert.house_name, "my-house");
764 assert_eq!(alert.brick_name, "slow-brick");
765 assert_eq!(alert.budget_ms, 100);
766 assert_eq!(alert.actual_ms, 200);
767 assert!(alert.phase.is_some());
768 assert!(alert.stack_trace.is_none());
769 assert_eq!(alert.overage_percent(), 100.0);
770 }
771
772 #[test]
773 fn test_brick_entry_debug_no_render_time() {
774 let brick = Arc::new(SimpleBrick { name: "test" });
775 let entry = BrickEntry {
776 brick,
777 allocated_ms: 100,
778 last_render_time: None,
779 };
780 let debug_str = format!("{:?}", entry);
781 assert!(debug_str.contains("None"));
782 }
783
784 struct FailingBrick {
786 name: &'static str,
787 }
788
789 impl Brick for FailingBrick {
790 fn brick_name(&self) -> &'static str {
791 self.name
792 }
793
794 fn assertions(&self) -> &[BrickAssertion] {
795 &[BrickAssertion::TextVisible]
796 }
797
798 fn budget(&self) -> BrickBudget {
799 BrickBudget::uniform(16)
800 }
801
802 fn verify(&self) -> BrickVerification {
803 BrickVerification {
804 passed: vec![],
805 failed: vec![(BrickAssertion::TextVisible, "Text not visible".to_string())],
806 verification_time: Duration::from_micros(1),
807 }
808 }
809
810 fn to_html(&self) -> String {
811 format!("<div class=\"{}\">{}</div>", self.name, self.name)
812 }
813
814 fn to_css(&self) -> String {
815 format!(".{} {{ display: block; }}", self.name)
816 }
817
818 fn can_render(&self) -> bool {
819 false
820 }
821 }
822
823 #[test]
824 fn test_brick_house_render_failing_brick() {
825 let brick = Arc::new(FailingBrick { name: "failing" });
826 let mut house = BrickHouse::new("test-house", 1000);
827 house.add_brick(brick, 100).unwrap();
828
829 let result = house.render();
830 assert!(result.is_err());
831 }
832
833 #[test]
834 fn test_brick_house_can_render_with_failing_brick() {
835 let brick = Arc::new(FailingBrick { name: "failing" });
836 let mut house = BrickHouse::new("test-house", 1000);
837 house.add_brick(brick, 100).unwrap();
838
839 assert!(!house.can_render());
840 }
841
842 #[test]
843 fn test_brick_house_multiple_bricks_render() {
844 let brick1 = Arc::new(SimpleBrick { name: "header" });
845 let brick2 = Arc::new(SimpleBrick { name: "content" });
846 let brick3 = Arc::new(SimpleBrick { name: "footer" });
847
848 let mut house = BrickHouse::new("page", 1000);
849 house.add_brick(brick1, 100).unwrap();
850 house.add_brick(brick2, 200).unwrap();
851 house.add_brick(brick3, 100).unwrap();
852
853 let html = house.render().unwrap();
854 assert!(html.contains("header"));
855 assert!(html.contains("content"));
856 assert!(html.contains("footer"));
857 }
858
859 #[test]
860 fn test_brick_house_builder_with_many_bricks() {
861 let brick1 = Arc::new(SimpleBrick { name: "a" });
862 let brick2 = Arc::new(SimpleBrick { name: "b" });
863 let brick3 = Arc::new(SimpleBrick { name: "c" });
864
865 let house = BrickHouseBuilder::new("multi")
866 .budget_ms(1000)
867 .brick(brick1, 100)
868 .brick(brick2, 200)
869 .brick(brick3, 300)
870 .build()
871 .unwrap();
872
873 assert_eq!(house.brick_count(), 3);
874 assert_eq!(house.remaining_budget_ms(), 400);
875 }
876
877 #[test]
878 fn test_budget_report_utilization_100_percent() {
879 let report = BudgetReport {
880 house_name: "test".into(),
881 total_budget_ms: 100,
882 total_used_ms: 100,
883 brick_timings: HashMap::new(),
884 violations: vec![],
885 timestamp: std::time::SystemTime::now(),
886 };
887 assert_eq!(report.utilization(), 100.0);
888 }
889
890 #[test]
891 fn test_budget_report_utilization_over_budget() {
892 let report = BudgetReport {
893 house_name: "test".into(),
894 total_budget_ms: 100,
895 total_used_ms: 200,
896 brick_timings: HashMap::new(),
897 violations: vec![],
898 timestamp: std::time::SystemTime::now(),
899 };
900 assert_eq!(report.utilization(), 200.0);
901 assert!(!report.within_budget());
903 }
904
905 #[test]
906 fn test_brick_house_empty_render() {
907 let mut house = BrickHouse::new("empty", 1000);
908 let html = house.render().unwrap();
909 assert!(html.is_empty());
910 }
911
912 #[test]
913 fn test_brick_house_empty_css() {
914 let house = BrickHouse::new("empty", 1000);
915 let css = house.to_css();
916 assert!(css.is_empty());
917 }
918
919 #[test]
920 fn test_brick_house_verify_all_empty() {
921 let house = BrickHouse::new("empty", 1000);
922 let verifications = house.verify_all();
923 assert!(verifications.is_empty());
924 }
925}