wick_test/
unit_test.rs

1use std::collections::{HashMap, HashSet, VecDeque};
2use std::time::{SystemTime, UNIX_EPOCH};
3
4use wick_config::config::test_case::TestCase;
5use wick_packet::{InherentData, Packet, PacketExt, PacketStream, RuntimeConfig};
6
7use crate::assertion_packet::{TestKind, ToPacket};
8use crate::operators::assert_packet;
9use crate::TestError;
10
11#[derive(Debug, Clone)]
12pub struct UnitTest<'a> {
13  pub test: &'a TestCase,
14  actual: HashMap<String, VecDeque<Packet>>,
15}
16
17impl<'a> UnitTest<'a> {
18  pub(crate) fn new(test: &'a TestCase) -> Self {
19    Self {
20      test,
21      actual: HashMap::new(),
22    }
23  }
24
25  pub fn set_actual(&mut self, actual: Vec<Packet>) {
26    for packet in actual {
27      self
28        .actual
29        .entry(packet.port().to_owned())
30        .or_default()
31        .push_back(packet);
32    }
33  }
34
35  pub(crate) fn check_next(&mut self, expected: &TestKind) -> Result<(), TestError> {
36    let packets = self
37      .actual
38      .get_mut(expected.port())
39      .ok_or(TestError::InvalidPort(expected.port().to_owned()))?;
40
41    #[allow(clippy::never_loop)]
42    while let Some(actual) = packets.pop_front() {
43      assert_packet(expected, actual)?;
44
45      return Ok(());
46    }
47    Ok(())
48  }
49
50  pub(crate) fn finalize(&mut self, _explicit_done: &HashSet<String>) -> Result<(), Vec<Packet>> {
51    let mut with_data = Vec::new();
52    let packets = self.actual.drain().collect::<Vec<_>>();
53    for (port, packets) in packets {
54      for packet in packets {
55        if packet.is_done() {
56          debug!(port, "test: received done packet without assertion, ignoring");
57          continue;
58        }
59        with_data.push(packet);
60        break;
61      }
62    }
63    if with_data.is_empty() {
64      Ok(())
65    } else {
66      Err(with_data)
67    }
68  }
69}
70
71pub(crate) fn get_payload(
72  test: &UnitTest,
73  root_config: Option<&RuntimeConfig>,
74  op_config: Option<&RuntimeConfig>,
75) -> Result<(PacketStream, InherentData, HashSet<String>), TestError> {
76  let mut packets = Vec::new();
77  let mut open_streams = HashSet::new();
78
79  // need a Vec to store the order of seen ports so we can have repeatable tests.
80  // HashSet doesn't guarantee order.
81  let mut order = Vec::new();
82
83  for packet in test.test.inputs() {
84    // if we've never seen this port before, push it onto the order Vec.
85    if !open_streams.contains(packet.port()) {
86      order.push(packet.port().to_owned());
87    }
88    open_streams.insert(packet.port().to_owned());
89  }
90
91  let mut explicit_done = HashSet::new();
92
93  if test.test.inputs().is_empty() {
94    packets.push(Packet::no_input());
95  } else {
96    for packet in test.test.inputs() {
97      let done = packet.flag().is_done();
98      if done {
99        explicit_done.insert(packet.port().to_owned());
100        open_streams.remove(packet.port());
101      } else if !open_streams.contains(packet.port()) {
102        return Err(TestError::PacketsAfterDone(packet.port().to_owned()));
103      }
104      debug!(?packet, "test packet");
105      packets.push(packet.to_packet(root_config, op_config)?);
106    }
107
108    // Add any missing "done" packets as a convenience to the test writer.
109    // (in the order we saw the ports)
110    for port in order {
111      if open_streams.contains(&port) {
112        debug!(input = port, "adding missing done packet for input");
113        packets.push(Packet::done(port));
114      }
115    }
116  }
117
118  let (seed, timestamp) = test.test.inherent().map_or_else(
119    || {
120      (
121        0,
122        SystemTime::now()
123          .duration_since(UNIX_EPOCH)
124          .unwrap()
125          .as_millis()
126          .try_into()
127          .unwrap(),
128      )
129    },
130    |inherent| {
131      let seed = inherent.seed().unwrap_or(0);
132      let timestamp = inherent.timestamp().unwrap_or(
133        SystemTime::now()
134          .duration_since(UNIX_EPOCH)
135          .unwrap()
136          .as_millis()
137          .try_into()
138          .unwrap(),
139      );
140      (seed, timestamp)
141    },
142  );
143
144  Ok((packets.into(), InherentData::new(seed, timestamp), explicit_done))
145}