| // Copyright 2020 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| use { | |
| crate::{ | |
| descriptor::EventDescriptor, | |
| events::{event_name, EventSource, EventStream}, | |
| matcher::EventMatcher, | |
| }, | |
| anyhow::{format_err, Error}, | |
| fuchsia_async as fasync, | |
| futures::{channel::oneshot, future::BoxFuture}, | |
| std::convert::TryFrom, | |
| }; | |
| /// Ordering is used by `EventSequence::subscribe_and_expect`to determine if it should allow events to be | |
| /// verified in any order or only in the order specified by the test. | |
| #[derive(Clone)] | |
| pub enum Ordering { | |
| Ordered, | |
| Unordered, | |
| } | |
| #[derive(Clone)] | |
| pub struct EventSequence { | |
| groups: Vec<EventGroup>, | |
| } | |
| impl EventSequence { | |
| pub fn new() -> Self { | |
| Self { groups: vec![] } | |
| } | |
| pub fn then(self, matcher: EventMatcher) -> Self { | |
| self.all_of(vec![matcher], Ordering::Ordered) | |
| } | |
| pub fn all_of(mut self, events: Vec<EventMatcher>, ordering: Ordering) -> Self { | |
| self.groups.push(EventGroup::new(events, ordering)); | |
| self | |
| } | |
| /// Verify that the events in this sequence are received from the provided | |
| /// EventStream. | |
| pub async fn expect(mut self, mut event_stream: EventStream) -> Result<(), Error> { | |
| while !self.groups.is_empty() { | |
| match event_stream.next().await { | |
| Err(e) => return Err(e.into()), | |
| Ok(event) => { | |
| let actual_event = EventDescriptor::try_from(&event)?; | |
| if actual_event.target_moniker != Some(".".to_string()) { | |
| let _ = self.next(&actual_event)?; | |
| } | |
| } | |
| } | |
| } | |
| Ok(()) | |
| } | |
| /// Verifies that the given events are received from the event system. Based on the vector of | |
| /// events passed in this function will subscribe to an event stream with the relevant event | |
| /// types and verify that the correct events for the component are received. | |
| pub async fn subscribe_and_expect<'a>( | |
| self, | |
| event_source: &mut EventSource, | |
| ) -> Result<BoxFuture<'a, Result<(), Error>>, Error> { | |
| let event_names = self.event_names()?; | |
| let event_stream = event_source.subscribe(event_names).await?; | |
| let expected_events = self.clone(); | |
| let (tx, rx) = oneshot::channel(); | |
| fasync::Task::spawn(async move { | |
| let res = expected_events.expect(event_stream).await; | |
| tx.send(res).expect("Unable to send result"); | |
| }) | |
| .detach(); | |
| Ok(Box::pin(async move { rx.await? })) | |
| } | |
| pub fn is_empty(&self) -> bool { | |
| self.groups.is_empty() | |
| } | |
| pub fn event_names(&self) -> Result<Vec<String>, Error> { | |
| let mut event_names = vec![]; | |
| for group in &self.groups { | |
| let mut group_event_names = group.event_names()?; | |
| event_names.append(&mut group_event_names); | |
| } | |
| event_names.dedup(); | |
| Ok(event_names) | |
| } | |
| // Takes in an EventDescriptor and tests it against the first EventGroup. | |
| // If an EventGroup has been entirely consumed (no further EventMatchers | |
| // to match against) then the EventGroup is dropped. If the | |
| // EventSequence have not been entirely consumed, and the incoming | |
| // EventDescriptor does not match the first sequence then an error is returned. | |
| // If the EventSequence is empty, then Ok(false) is returned. Otherwise, if | |
| // there is a positive match, then Ok(true) is returned. | |
| pub fn next(&mut self, event: &EventDescriptor) -> Result<(), Error> { | |
| loop { | |
| if self.groups.is_empty() { | |
| return Ok(()); | |
| } | |
| let group = &mut self.groups[0]; | |
| if group.next(event)? { | |
| if group.is_empty() { | |
| self.groups.remove(0); | |
| } | |
| return Ok(()); | |
| } | |
| self.groups.remove(0); | |
| } | |
| } | |
| } | |
| #[derive(Clone)] | |
| pub struct EventGroup { | |
| events: Vec<EventMatcher>, | |
| ordering: Ordering, | |
| } | |
| impl EventGroup { | |
| pub fn new(events: Vec<EventMatcher>, ordering: Ordering) -> Self { | |
| Self { events, ordering } | |
| } | |
| pub fn is_empty(&self) -> bool { | |
| self.events.is_empty() | |
| } | |
| pub fn event_names(&self) -> Result<Vec<String>, Error> { | |
| let mut event_names = vec![]; | |
| for event in &self.events { | |
| if let Some(event_type) = &event.event_type { | |
| event_names.push(event_name(&event_type.value())); | |
| } else { | |
| return Err(format_err!("No event name or type set for matcher {:?}", event)); | |
| } | |
| } | |
| event_names.dedup(); | |
| Ok(event_names) | |
| } | |
| // Takes in an EventDescriptor and tests it against the next EventMatcher in | |
| // the sequence. If the sequence is empty, then Ok(false) is returned. If | |
| // the sequence is Ordered, then this tests it against the first | |
| // EventMatcher. If the sequence is Unordered, then this tests the | |
| // EventDescriptor against all EventMatchers in the sequence to find a | |
| // match. In either case, if no match is found, then an Error is returned. | |
| // If there is a positive match, then Ok(true) is returned. | |
| pub fn next(&mut self, event: &EventDescriptor) -> Result<bool, Error> { | |
| if self.events.is_empty() { | |
| return Ok(false); | |
| } | |
| let expected_event = match self.ordering { | |
| Ordering::Ordered => self.events.remove(0), | |
| Ordering::Unordered => { | |
| if let Some((index, _)) = self | |
| .events | |
| .iter() | |
| .enumerate() | |
| .find(|&matcher| matcher.1.matches(&event).is_ok()) | |
| { | |
| self.events.remove(index) | |
| } else { | |
| return Err(format_err!("Failed to find event: {:?}", event)); | |
| } | |
| } | |
| }; | |
| expected_event.matches(&event).map(|_| true) | |
| } | |
| } |