1#[macro_use]
7extern crate log;
8
9use rand::seq::SliceRandom;
10use rand_xorshift::XorShiftRng;
11use structopt::StructOpt;
12
13use abstutil::{prettyprint_usize, Timer};
14use geom::Duration;
15use map_model::{LaneID, LaneType, Map, MapEdits};
16use sim::Sim;
17
18#[derive(StructOpt)]
19#[structopt(
20 name = "traffic_seitan",
21 about = "Automated fuzz testing for live map edits"
22)]
23struct Args {
24 #[structopt(flatten)]
25 flags: sim::SimFlags,
26}
27
28fn main() {
29 abstutil::logger::setup();
30 let mut sim_flags = Args::from_args().flags;
31 sim_flags.initialize();
32
33 let mut timer = Timer::throwaway();
34 let (mut map, mut sim, mut rng) = sim_flags.load_synchronously(&mut timer);
35
36 {
38 let mut edits = map.get_edits().clone();
39 edits.edits_name = "traffic_seitan".to_string();
40 map.must_apply_edits(edits, &mut timer);
41 map.recalculate_pathfinding_after_edits(&mut timer);
42 sim.handle_live_edits(&map, &mut timer);
43 }
44
45 if let Err(err) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
46 run(&mut map, &mut sim, &mut rng, &mut timer);
47 })) {
48 let mut edits = map.get_edits().clone();
49 edits.edits_name = "traffic_seitan_crash".to_string();
50 map.must_apply_edits(edits, &mut timer);
51 map.save_edits();
52
53 println!("Crashed at {}", sim.time());
54
55 std::panic::resume_unwind(err)
56 }
57}
58
59fn run(map: &mut Map, sim: &mut Sim, rng: &mut XorShiftRng, timer: &mut Timer) {
60 let edit_frequency = Duration::minutes(5);
61
62 while !sim.is_done() {
63 println!();
64 sim.timed_step(map, edit_frequency, &mut None, timer);
65 sim.save();
66 map.save_edits();
67
68 let mut edits = map.get_edits().clone();
69 nuke_random_parking(map, rng, &mut edits);
70 alter_turn_destinations(sim, map, rng, &mut edits);
71
72 map.must_apply_edits(edits, timer);
73 map.recalculate_pathfinding_after_edits(timer);
74 sim.handle_live_edited_traffic_signals(map);
75 sim.handle_live_edits(map, timer);
76 }
77
78 let mut finished = 0;
79 let mut cancelled = 0;
80 for (_, _, _, maybe_dt) in &sim.get_analytics().finished_trips {
81 if maybe_dt.is_some() {
82 finished += 1;
83 } else {
84 cancelled += 1;
85 }
86 }
87 println!(
88 "\nDone! {} finished trips, {} cancelled",
89 prettyprint_usize(finished),
90 prettyprint_usize(cancelled)
91 );
92}
93
94fn alter_turn_destinations(sim: &Sim, map: &Map, rng: &mut XorShiftRng, edits: &mut MapEdits) {
95 let num_edits = 3;
96
97 let mut active_destinations = Vec::new();
99 for i in map.all_intersections() {
100 for (_, t) in sim.get_accepted_agents(i.id) {
101 if !map.get_l(t.dst).is_walkable() {
102 active_destinations.push(t.dst);
103 }
104 }
105 }
106 active_destinations.sort();
107 active_destinations.dedup();
108 active_destinations.shuffle(rng);
109
110 for l in active_destinations.into_iter().take(num_edits) {
111 info!("Closing someone's target {}", l);
112 edits.commands.push(map.edit_road_cmd(l.road, |new| {
113 new.lanes_ltr[l.offset].lt = LaneType::Construction;
114
115 if new
118 .lanes_ltr
119 .iter()
120 .all(|spec| spec.lt != LaneType::Driving)
121 {
122 for spec in &mut new.lanes_ltr {
123 if spec.lt == LaneType::Parking {
124 spec.lt = LaneType::Construction;
125 }
126 }
127 }
128 }));
129 }
130}
131
132fn nuke_random_parking(map: &Map, rng: &mut XorShiftRng, edits: &mut MapEdits) {
133 let num_edits = 5;
134
135 let mut parking_lanes: Vec<LaneID> = map
136 .all_lanes()
137 .filter(|l| l.is_parking())
138 .map(|l| l.id)
139 .collect();
140 parking_lanes.shuffle(rng);
141 for l in parking_lanes.into_iter().take(num_edits) {
142 info!("Closing parking {}", l);
143 edits.commands.push(map.edit_road_cmd(l.road, |new| {
144 new.lanes_ltr[l.offset].lt = LaneType::Construction;
145 }));
146 }
147}