run_test_suite_lib/output/
mux.rs1use crate::output::{
6 ArtifactType, DirectoryArtifactType, DirectoryWrite, DynArtifact, DynDirectoryArtifact,
7 EntityId, EntityInfo, ReportedOutcome, Reporter, Timestamp,
8};
9use std::io::{Error, Write};
10use std::path::Path;
11
12pub struct MultiplexedWriter<A: Write, B: Write> {
14 a: A,
15 b: B,
16}
17
18impl<A: Write, B: Write> Write for MultiplexedWriter<A, B> {
19 fn write(&mut self, bytes: &[u8]) -> Result<usize, Error> {
20 let bytes_written = self.a.write(bytes)?;
21 self.b.write_all(&bytes[..bytes_written])?;
24 Ok(bytes_written)
25 }
26
27 fn flush(&mut self) -> Result<(), Error> {
28 self.a.flush()?;
29 self.b.flush()
30 }
31}
32
33impl<A: Write, B: Write> MultiplexedWriter<A, B> {
34 pub fn new(a: A, b: B) -> Self {
35 Self { a, b }
36 }
37}
38
39pub struct MultiplexedReporter<A: Reporter, B: Reporter> {
41 a: A,
42 b: B,
43}
44
45impl<A: Reporter, B: Reporter> MultiplexedReporter<A, B> {
46 pub fn new(a: A, b: B) -> Self {
47 Self { a, b }
48 }
49}
50
51impl<A: Reporter, B: Reporter> Reporter for MultiplexedReporter<A, B> {
52 fn new_entity(&self, entity: &EntityId, name: &str) -> Result<(), Error> {
53 self.a.new_entity(entity, name)?;
54 self.b.new_entity(entity, name)
55 }
56
57 fn set_entity_info(&self, entity: &EntityId, info: &EntityInfo) {
58 self.a.set_entity_info(entity, info);
59 self.b.set_entity_info(entity, info)
60 }
61
62 fn entity_started(&self, entity: &EntityId, timestamp: Timestamp) -> Result<(), Error> {
63 self.a.entity_started(entity, timestamp)?;
64 self.b.entity_started(entity, timestamp)
65 }
66
67 fn entity_stopped(
68 &self,
69 entity: &EntityId,
70 outcome: &ReportedOutcome,
71 timestamp: Timestamp,
72 ) -> Result<(), Error> {
73 self.a.entity_stopped(entity, outcome, timestamp)?;
74 self.b.entity_stopped(entity, outcome, timestamp)
75 }
76
77 fn entity_finished(&self, entity: &EntityId) -> Result<(), Error> {
78 self.a.entity_finished(entity)?;
79 self.b.entity_finished(entity)
80 }
81
82 fn new_artifact(
83 &self,
84 entity: &EntityId,
85 artifact_type: &ArtifactType,
86 ) -> Result<Box<DynArtifact>, Error> {
87 let a = self.a.new_artifact(entity, artifact_type)?;
88 let b = self.b.new_artifact(entity, artifact_type)?;
89 Ok(Box::new(MultiplexedWriter::new(a, b)))
90 }
91
92 fn new_directory_artifact(
93 &self,
94 entity: &EntityId,
95 artifact_type: &DirectoryArtifactType,
96 component_moniker: Option<String>,
97 ) -> Result<Box<DynDirectoryArtifact>, Error> {
98 let a = self.a.new_directory_artifact(entity, artifact_type, component_moniker.clone())?;
99 let b = self.b.new_directory_artifact(entity, artifact_type, component_moniker)?;
100 Ok(Box::new(MultiplexedDirectoryWriter { a, b }))
101 }
102}
103
104pub(super) struct MultiplexedDirectoryWriter {
106 a: Box<DynDirectoryArtifact>,
107 b: Box<DynDirectoryArtifact>,
108}
109
110impl MultiplexedDirectoryWriter {
111 pub(super) fn new(a: Box<DynDirectoryArtifact>, b: Box<DynDirectoryArtifact>) -> Self {
112 Self { a, b }
113 }
114}
115
116impl DirectoryWrite for MultiplexedDirectoryWriter {
117 fn new_file(&self, path: &Path) -> Result<Box<DynArtifact>, Error> {
118 Ok(Box::new(MultiplexedWriter::new(self.a.new_file(path)?, self.b.new_file(path)?)))
119 }
120}
121
122#[cfg(test)]
123mod test {
124 use super::*;
125 use crate::output::directory::{DirectoryReporter, SchemaVersion};
126 use crate::output::{RunReporter, SuiteId};
127 use tempfile::tempdir;
128 use test_output_directory as directory;
129 use test_output_directory::testing::{
130 assert_run_result, ExpectedDirectory, ExpectedSuite, ExpectedTestRun,
131 };
132
133 #[fuchsia::test]
134 fn multiplexed_writer() {
135 const WRITTEN: &str = "test output";
136
137 let mut buf_1: Vec<u8> = vec![];
138 let mut buf_2: Vec<u8> = vec![];
139 let mut multiplexed_writer = MultiplexedWriter::new(&mut buf_1, &mut buf_2);
140
141 multiplexed_writer.write_all(WRITTEN.as_bytes()).expect("write_all failed");
142 assert_eq!(std::str::from_utf8(&buf_1).unwrap(), WRITTEN);
143 assert_eq!(std::str::from_utf8(&buf_2).unwrap(), WRITTEN);
144 }
145
146 #[fuchsia::test]
147 fn multiplexed_reporter() {
148 let tempdir_1 = tempdir().expect("create temp directory");
149 let reporter_1 = DirectoryReporter::new(tempdir_1.path().to_path_buf(), SchemaVersion::V1)
150 .expect("Create reporter");
151 let tempdir_2 = tempdir().expect("create temp directory");
152 let reporter_2 = DirectoryReporter::new(tempdir_2.path().to_path_buf(), SchemaVersion::V1)
153 .expect("Create reporter");
154 let multiplexed_reporter = MultiplexedReporter::new(reporter_1, reporter_2);
155
156 let run_reporter = RunReporter::new(multiplexed_reporter);
157 run_reporter.started(Timestamp::Unknown).expect("start run");
158 let mut run_artifact =
159 run_reporter.new_artifact(&ArtifactType::Stdout).expect("create artifact");
160 writeln!(run_artifact, "run artifact contents").expect("write to run artifact");
161 run_artifact.flush().expect("flush run artifact");
162
163 let suite_reporter = run_reporter.new_suite("suite", &SuiteId(0)).expect("create suite");
164 suite_reporter.started(Timestamp::Unknown).expect("start suite");
165 suite_reporter.stopped(&ReportedOutcome::Passed, Timestamp::Unknown).expect("start suite");
166 let suite_dir_artifact = suite_reporter
167 .new_directory_artifact(&DirectoryArtifactType::Custom, None)
168 .expect("new artifact");
169 let mut suite_artifact =
170 suite_dir_artifact.new_file("test.txt".as_ref()).expect("create suite artifact file");
171 writeln!(suite_artifact, "suite artifact contents").expect("write to suite artifact");
172 suite_artifact.flush().expect("flush suite artifact");
173 suite_reporter.finished().expect("finish suite");
174
175 run_reporter.stopped(&ReportedOutcome::Passed, Timestamp::Unknown).expect("stop run");
176 run_reporter.finished().expect("finish run");
177
178 let expected_run = ExpectedTestRun::new(directory::Outcome::Passed)
179 .with_artifact(
180 directory::ArtifactType::Stdout,
181 Option::<&str>::None,
182 "run artifact contents\n",
183 )
184 .with_suite(
185 ExpectedSuite::new("suite", directory::Outcome::Passed).with_directory_artifact(
186 directory::ArtifactType::Custom,
187 Option::<&str>::None,
188 ExpectedDirectory::new().with_file("test.txt", "suite artifact contents\n"),
189 ),
190 );
191
192 assert_run_result(tempdir_1.path(), &expected_run);
194 assert_run_result(tempdir_2.path(), &expected_run);
195 }
196}